From 73b2030d83a598b2114c749103b4ee115c5bd3dc Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 29 Apr 2021 10:15:27 -0400 Subject: [PATCH 01/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/Case.java | 114 +++------------- .../casemodule/events/HostsChangedEvent.java | 41 ------ .../autopsy/casemodule/events/HostsEvent.java | 28 +--- .../casemodule/events/HostsRemovedEvent.java | 39 ------ .../events/OsAccountAddedEvent.java | 35 ----- .../events/OsAccountChangedEvent.java | 34 ----- .../events/OsAccountDeletedEvent.java | 37 ------ .../casemodule/events/OsAccountEvent.java | 64 --------- .../casemodule/events/PersonsAddedEvent.java | 16 ++- .../events/PersonsChangedEvent.java | 41 ------ .../events/PersonsDeletedEvent.java | 27 ++-- .../casemodule/events/PersonsEvent.java | 34 +---- .../casemodule/events/ReportAddedEvent.java | 4 +- .../events/TskDataModelChangeEvent.java | 124 ------------------ .../sleuthkit/autopsy/datamodel/HostNode.java | 6 +- .../autopsy/datamodel/OsAccounts.java | 6 +- .../autopsy/datamodel/PersonGroupingNode.java | 6 +- 17 files changed, 66 insertions(+), 590 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsChangedEvent.java delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsChangedEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangeEvent.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index ddf8cc22b9..f77859d407 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -84,13 +84,13 @@ import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent; -import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent; -import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; -import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent; +import org.sleuthkit.autopsy.casemodule.events.HostsDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.OsAccountsAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent; +import org.sleuthkit.autopsy.casemodule.events.OsAccountsDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent; -import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent; +import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; @@ -500,24 +500,18 @@ public class Case { } @Subscribe - public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) { - for (OsAccount account : event.getOsAcounts()) { - eventPublisher.publish(new OsAccountAddedEvent(account)); - } + public void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event) { + eventPublisher.publish(new OsAccountsAddedEvent(event.getOsAcounts())); } @Subscribe - public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) { - for (OsAccount account : event.getOsAcounts()) { - eventPublisher.publish(new OsAccountChangedEvent(account)); - } + public void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event) { + eventPublisher.publish(new OsAccountsUpdatedEvent(event.getOsAcounts())); } @Subscribe - public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) { - for (Long accountId : event.getOsAcountObjectIds()) { - eventPublisher.publish(new OsAccountDeletedEvent(accountId)); - } + public void publishOsAccountsDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) { + eventPublisher.publish(new OsAccountsDeletedEvent(event.getObjectIds())); } /** @@ -528,8 +522,7 @@ public class Case { */ @Subscribe public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) { - eventPublisher.publish(new HostsAddedEvent( - event == null ? Collections.emptyList() : event.getHosts())); + eventPublisher.publish(new HostsAddedEvent(event.getHosts())); } /** @@ -539,9 +532,8 @@ public class Case { * @param event The sleuthkit event for the updating of hosts. */ @Subscribe - public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) { - eventPublisher.publish(new HostsChangedEvent( - event == null ? Collections.emptyList() : event.getHosts())); + public void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event) { + eventPublisher.publish(new HostsUpdatedEvent(event.getHosts())); } /** @@ -552,8 +544,7 @@ public class Case { */ @Subscribe public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) { - eventPublisher.publish(new HostsRemovedEvent( - event == null ? Collections.emptyList() : event.getHosts())); + eventPublisher.publish(new HostsDeletedEvent(event.getObjectIds())); } /** @@ -575,8 +566,8 @@ public class Case { * @param event The sleuthkit event for the updating of persons. */ @Subscribe - public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) { - eventPublisher.publish(new PersonsChangedEvent( + public void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event) { + eventPublisher.publish(new PersonsUpdatedEvent( event == null ? Collections.emptyList() : event.getPersons())); } @@ -588,8 +579,7 @@ public class Case { */ @Subscribe public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) { - eventPublisher.publish(new PersonsDeletedEvent( - event == null ? Collections.emptyList() : event.getPersons())); + eventPublisher.publish(new PersonsDeletedEvent(event.getObjectIds())); } } @@ -1826,72 +1816,6 @@ public class Case { eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag)); } - public void notifyOsAccountAdded(OsAccount account) { - eventPublisher.publish(new OsAccountAddedEvent(account)); - } - - public void notifyOsAccountChanged(OsAccount account) { - eventPublisher.publish(new OsAccountChangedEvent(account)); - } - - public void notifyOsAccountRemoved(Long osAccountObjectId) { - eventPublisher.publish(new OsAccountDeletedEvent(osAccountObjectId)); - } - - /** - * Notify via an autopsy event that a host has been added. - * - * @param host The host that has been added. - */ - public void notifyHostAdded(Host host) { - eventPublisher.publish(new HostsAddedEvent(Collections.singletonList(host))); - } - - /** - * Notify via an autopsy event that a host has been changed. - * - * @param newValue The host that has been updated. - */ - public void notifyHostChanged(Host newValue) { - eventPublisher.publish(new HostsChangedEvent(Collections.singletonList(newValue))); - } - - /** - * Notify via an autopsy event that a host has been deleted. - * - * @param host The host that has been deleted. - */ - public void notifyHostDeleted(Host host) { - eventPublisher.publish(new HostsRemovedEvent(Collections.singletonList(host))); - } - - /** - * Notify via an autopsy event that a person has been added. - * - * @param person The person that has been added. - */ - public void notifyPersonAdded(Person person) { - eventPublisher.publish(new PersonsAddedEvent(Collections.singletonList(person))); - } - - /** - * Notify via an autopsy event that a person has been changed. - * - * @param newValue The person that has been updated. - */ - public void notifyPersonChanged(Person newValue) { - eventPublisher.publish(new PersonsChangedEvent(Collections.singletonList(newValue))); - } - - /** - * Notify via an autopsy event that a person has been deleted. - * - * @param person The person that has been deleted. - */ - public void notifyPersonDeleted(Person person) { - eventPublisher.publish(new PersonsDeletedEvent(Collections.singletonList(person))); - } - /** * Adds a report to the case. * diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsChangedEvent.java deleted file mode 100644 index a5b8692c03..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsChangedEvent.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import java.util.List; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.Host; - -/** - * Event fired when hosts are changed. - */ -public class HostsChangedEvent extends HostsEvent { - - private static final long serialVersionUID = 1L; - - /** - * Main constructor. - * - * @param dataModelObjects The new values for the hosts that have been - * changed. - */ - public HostsChangedEvent(List dataModelObjects) { - super(Case.Events.HOSTS_CHANGED.name(), dataModelObjects); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java index 7c9a31f01e..69f68c0b4e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java @@ -19,10 +19,8 @@ package org.sleuthkit.autopsy.casemodule.events; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.HostManager; import org.sleuthkit.datamodel.SleuthkitCase; @@ -31,32 +29,10 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Base event class for when something pertaining to hosts changes. */ -public class HostsEvent extends TskDataModelChangeEvent { +public class HostsEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; - /** - * Retrieves a list of ids from a list of hosts. - * - * @param hosts The hosts. - * @return The list of ids. - */ - private static List getIds(List hosts) { - return getSafeList(hosts).stream() - .filter(h -> h != null) - .map(h -> h.getHostId()).collect(Collectors.toList()); - } - - /** - * Returns the hosts or an empty list. - * - * @param hosts The host list. - * @return The host list or an empty list if the parameter is null. - */ - private static List getSafeList(List hosts) { - return hosts == null ? Collections.emptyList() : hosts; - } - /** * Main constructor. * @@ -65,7 +41,7 @@ public class HostsEvent extends TskDataModelChangeEvent { * @param dataModelObjects The list of hosts for the event. */ protected HostsEvent(String eventName, List dataModelObjects) { - super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects))); + super(eventName, dataModelObjects, Host::getHostId); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedEvent.java deleted file mode 100644 index 407b83c32a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import java.util.List; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.Host; - -/** - * Event fired when hosts are removed. - */ -public class HostsRemovedEvent extends HostsEvent { - - private static final long serialVersionUID = 1L; - - /** - * Main constructor. - * @param dataModelObjects The list of hosts that have been deleted. - */ - public HostsRemovedEvent(List dataModelObjects) { - super(Case.Events.HOSTS_DELETED.name(), dataModelObjects); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java deleted file mode 100755 index c9f89903f4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountAddedEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.OsAccount; - -/** - * Event published when an OsAccount is added to a case. - */ -public final class OsAccountAddedEvent extends OsAccountEvent { - - private static final long serialVersionUID = 1L; - - public OsAccountAddedEvent(OsAccount account) { - super(Case.Events.OS_ACCOUNT_ADDED.toString(), account); - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java deleted file mode 100755 index 237373a8b9..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountChangedEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.OsAccount; - -/** - * Event published when an OsAccount is updated. - */ -public final class OsAccountChangedEvent extends OsAccountEvent { - - private static final long serialVersionUID = 1L; - - public OsAccountChangedEvent(OsAccount account) { - super(Case.Events.OS_ACCOUNT_CHANGED.toString(), account); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java deleted file mode 100644 index adc726fca8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountDeletedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.events.AutopsyEvent; - -/** - * Event published when an OsAccount is deleted. - * - * oldValue will contain the objectId of the account that was removed. newValue - * will be null. - */ -public final class OsAccountDeletedEvent extends AutopsyEvent { - - private static final long serialVersionUID = 1L; - - public OsAccountDeletedEvent(Long osAccountObjectId) { - super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java deleted file mode 100755 index d34da7822d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountEvent.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.sleuthkit.datamodel.OsAccount; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Parent class for specific OsAccount event classes. - */ -class OsAccountEvent extends TskDataModelChangeEvent { - - private static final long serialVersionUID = 1L; - - /** - * Construct a new OsAccountEvent. - * - * @param eventName The name of the event. - * @param account The OsAccount the event applies to. - */ - OsAccountEvent(String eventName, OsAccount account) { - super(eventName, Stream.of(account.getId()).collect(Collectors.toList()), Stream.of(account).collect(Collectors.toList())); - } - - /** - * Returns the OsAccount that changed. - * - * @return The OsAccount that was changed. - */ - public OsAccount getOsAccount() { - List accounts = getNewValue(); - return accounts.get(0); - } - - @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - Long id = ids.get(0); - OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id); - List accounts = new ArrayList<>(); - accounts.add(account); - return accounts; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java index e2a8a7aabd..c61bc177f3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java @@ -23,17 +23,19 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Person; /** - * Event fired when new persons are added. + * Event fired when new persons are added to a case. */ public class PersonsAddedEvent extends PersonsEvent { - + private static final long serialVersionUID = 1L; - + /** - * Main constructor. - * @param dataModelObjects The persons that have been added. + * Constructs an event fired when new persons are added to a case. + * + * @param persons The persons that have been added. */ - public PersonsAddedEvent(List dataModelObjects) { - super(Case.Events.PERSONS_ADDED.name(), dataModelObjects); + public PersonsAddedEvent(List persons) { + super(Case.Events.PERSONS_ADDED.name(), persons); } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsChangedEvent.java deleted file mode 100644 index f375a125bb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsChangedEvent.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import java.util.List; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.Person; - -/** - * Event fired when persons are changed. - */ -public class PersonsChangedEvent extends PersonsEvent { - - private static final long serialVersionUID = 1L; - - /** - * Main constructor. - * - * @param dataModelObjects The new values for the persons that have been - * changed. - */ - public PersonsChangedEvent(List dataModelObjects) { - super(Case.Events.PERSONS_CHANGED.name(), dataModelObjects); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java index a63fef32cb..db80759214 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java @@ -18,22 +18,33 @@ */ package org.sleuthkit.autopsy.casemodule.events; +import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Person; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** - * Event fired when persons are removed. + * An event fired when persons are deleted from the case. */ -public class PersonsDeletedEvent extends PersonsEvent { - +public class PersonsDeletedEvent extends TskDataModelChangedEvent { + private static final long serialVersionUID = 1L; - + /** - * Main constructor. - * @param dataModelObjects The list of persons that have been deleted. + * Constructs an event fired when persons are deleted from the case. + * + * @param dataModelObjectIds The unique numeric IDs (case database row IDs) + * of the persons that have been deleted. */ - public PersonsDeletedEvent(List dataModelObjects) { - super(Case.Events.PERSONS_DELETED.name(), dataModelObjects); + public PersonsDeletedEvent(List dataModelObjectIds) { + super(Case.Events.PERSONS_DELETED.name(), dataModelObjectIds); } + + @Override + protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return Collections.emptyList(); + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java index e3f584d58a..5380ee4874 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java @@ -19,10 +19,8 @@ package org.sleuthkit.autopsy.casemodule.events; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.PersonManager; import org.sleuthkit.datamodel.SleuthkitCase; @@ -31,30 +29,10 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Base event class for when something pertaining to persons changes. */ -public class PersonsEvent extends TskDataModelChangeEvent { - - /** - * Retrieves a list of ids from a list of persons. - * - * @param persons The persons. - * @return The list of ids. - */ - private static List getIds(List persons) { - return getSafeList(persons).stream() - .filter(h -> h != null) - .map(h -> h.getPersonId()).collect(Collectors.toList()); - } - - /** - * Returns the persons or an empty list. - * - * @param persons The person list. - * @return The person list or an empty list if the parameter is null. - */ - private static List getSafeList(List persons) { - return persons == null ? Collections.emptyList() : persons; - } +public class PersonsEvent extends TskDataModelChangedEvent { + private static final long serialVersionUID = 1L; + /** * Main constructor. * @@ -62,10 +40,10 @@ public class PersonsEvent extends TskDataModelChangeEvent { * type. * @param dataModelObjects The list of persons for the event. */ - protected PersonsEvent(String eventName, List dataModelObjects) { - super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects))); + PersonsEvent(String eventName, List dataModelObjects) { + super(eventName, dataModelObjects, Person::getPersonId); } - + @Override protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { PersonManager personManager = caseDb.getPersonManager(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java index bb4145c804..47c5a30d5d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java @@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Event published when a report is added to a case. */ -public final class ReportAddedEvent extends TskDataModelChangeEvent { +public final class ReportAddedEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; @@ -40,7 +40,7 @@ public final class ReportAddedEvent extends TskDataModelChangeEvent { * @param report The data source that was added. */ public ReportAddedEvent(Report report) { - super(Case.Events.REPORT_ADDED.toString(), Stream.of(report.getId()).collect(Collectors.toList()), Stream.of(report).collect(Collectors.toList())); + super(Case.Events.REPORT_ADDED.toString(), Stream.of(report).collect(Collectors.toList()), Report::getId); } public Report getReport() { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangeEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangeEvent.java deleted file mode 100755 index 1bd9fd81ac..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangeEvent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.casemodule.events; - -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * An application event generic used as a superclass for events published when - * something changes in the Sleuth Kit Data Model for a case. - * - * @param A Sleuth Kit Data Model object type. - */ -public abstract class TskDataModelChangeEvent extends AutopsyEvent { - - private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(TskDataModelChangeEvent.class.getName()); - private final List dataModelObjectIds; - private transient List dataModelObjects; - - /** - * Constructs an application event generic used as a superclass for events - * published when something changes in the Sleuth Kit Data Model for a case. - * - * @param eventName The event name. - * @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case - * database row IDs, etc.) of the Sleuth Kit Data - * Model objects associated with this application - * event. - * @param dataModelObjects The Sleuth Kit Data Model objects associated - * with this application event - */ - protected TskDataModelChangeEvent(String eventName, List dataModelObjectIds, List dataModelObjects) { - super(eventName, null, null); - this.dataModelObjectIds = dataModelObjectIds; - this.dataModelObjects = dataModelObjects; - if (eventName == null) { - throw new IllegalArgumentException("eventName is null"); - } - if (dataModelObjectIds == null) { - throw new IllegalArgumentException("dataModelObjectIds is null"); - } - if (dataModelObjects == null) { - throw new IllegalArgumentException("dataModelObjects is null"); - } - } - - /** - * Gets the unique numeric IDs (TSK object IDs, case database row IDs, etc.) - * of the Sleuth Kit Data Model objects associated with this application - * event. - * - * @return The unique IDs. - */ - public final List getDataModelObjectIds() { - return Collections.unmodifiableList(dataModelObjectIds); - } - - /** - * Gets the Sleuth Kit Data Model objects associated with this application - * event. - * - * @return The objects. - */ - @Override - public List getNewValue() { - /* - * If this event came from another host collaborating on a multi-user - * case, the transient list of Sleuth Kit Data Model objects will be - * null and will need to be reconstructed on the current host. - */ - if (dataModelObjects == null) { - try { - Case currentCase = Case.getCurrentCaseThrows(); - SleuthkitCase caseDb = currentCase.getSleuthkitCase(); - dataModelObjects = getDataModelObjects(caseDb, dataModelObjectIds); - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error geting TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex); - return Collections.emptyList(); - } - } - return Collections.unmodifiableList(dataModelObjects); - } - - /** - * Gets the Sleuth Kit Data Model objects associated with this application - * event. - * - * @param caseDb The case database. - * @param ids The unique, numeric IDs (TSK object IDs, case database row - * IDs, etc.) of the Sleuth Kit Data Model objects. - * - * @return The objects. - * - * @throws org.sleuthkit.datamodel.TskCoreException If there is an error - * getting the Sleuth Kit - * Data Model objects. - */ - abstract protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException; - -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java index a98bdb61dd..57253ef60e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java @@ -38,7 +38,7 @@ import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent; +import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction; import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction; @@ -177,8 +177,8 @@ public class HostNode extends DisplayableItemNode { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) { - ((HostsChangedEvent) evt).getNewValue().stream() + if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsUpdatedEvent) { + ((HostsUpdatedEvent) evt).getNewValue().stream() .filter(h -> h != null && h.getHostId() == hostId) .findFirst() .ifPresent((newHost) -> { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 170251bd13..42a69caa99 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -39,7 +39,7 @@ import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; +import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; @@ -184,9 +184,9 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) { - if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) { + if (((OsAccountsUpdatedEvent) evt).getOsAccount().getId() == account.getId()) { // Update the account node to the new one - account = ((OsAccountChangedEvent) evt).getOsAccount(); + account = ((OsAccountsUpdatedEvent) evt).getOsAccount(); updateSheet(); } } else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java index 4f12779ee8..3934ffa06c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java @@ -36,7 +36,7 @@ import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent; +import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.persons.DeletePersonAction; import org.sleuthkit.autopsy.datamodel.persons.EditPersonAction; @@ -145,8 +145,8 @@ public class PersonGroupingNode extends DisplayableItemNode { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) { - ((PersonsChangedEvent) evt).getNewValue().stream() + if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsUpdatedEvent) { + ((PersonsUpdatedEvent) evt).getNewValue().stream() .filter(p -> p != null && p.getPersonId() == personId) .findFirst() .ifPresent((newPerson) -> { From 34c72cdc3469adfcad1a19defc7d18a322548d09 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 29 Apr 2021 10:17:34 -0400 Subject: [PATCH 02/78] 7553 data model event changes --- .../casemodule/events/HostsDeletedEvent.java | 51 ++++++ .../casemodule/events/HostsUpdatedEvent.java | 41 +++++ .../events/OsAccountsAddedEvent.java | 40 +++++ .../events/OsAccountsDeletedEvent.java | 49 ++++++ .../casemodule/events/OsAccountsEvent.java | 64 +++++++ .../events/OsAccountsUpdatedEvent.java | 41 +++++ .../events/PersonsUpdatedEvent.java | 41 +++++ .../events/TskDataModelChangedEvent.java | 165 ++++++++++++++++++ 8 files changed, 492 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java new file mode 100755 index 0000000000..3d62b78405 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java @@ -0,0 +1,51 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.Collections; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Event fired when hosts are deleted. + */ +public class HostsDeletedEvent extends TskDataModelChangedEvent { + + private static final long serialVersionUID = 1L; + + /** + * Main constructor. + * + * @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case + * database row IDs, etc.) of the Hosts that have + * been deleted. + */ + public HostsDeletedEvent(List dataModelObjectIds) { + super(Case.Events.HOSTS_DELETED.name(), dataModelObjectIds); + } + + @Override + protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return Collections.emptyList(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java new file mode 100755 index 0000000000..6b06382c0a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java @@ -0,0 +1,41 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Host; + +/** + * Event fired when hosts are changed. + */ +public class HostsUpdatedEvent extends HostsEvent { + + private static final long serialVersionUID = 1L; + + /** + * Main constructor. + * + * @param dataModelObjects The new values for the hosts that have been + * changed. + */ + public HostsUpdatedEvent(List dataModelObjects) { + super(Case.Events.HOSTS_CHANGED.name(), dataModelObjects); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java new file mode 100755 index 0000000000..49a722062b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java @@ -0,0 +1,40 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.OsAccount; + +/** + * Event published when OS accounts are added to a case. + */ +public final class OsAccountsAddedEvent extends OsAccountsEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an event published when OS accounts are added to a case. + * @param accounts + */ + public OsAccountsAddedEvent(List accounts) { + super(Case.Events.OS_ACCOUNT_ADDED.toString(), accounts); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java new file mode 100755 index 0000000000..52fe8d49a8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java @@ -0,0 +1,49 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.Collections; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.OsAccount; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * An event published when OS accounts are deleted from a case. + */ +public final class OsAccountsDeletedEvent extends TskDataModelChangedEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an event published when OS accounts are deleted from a case. + * + * @param osAccountObjectIds The object IDs of the deleted accounts. + */ + public OsAccountsDeletedEvent(List osAccountObjectIds) { + super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectIds); + } + + @Override + protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return Collections.emptyList(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java new file mode 100755 index 0000000000..af214776a8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java @@ -0,0 +1,64 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sleuthkit.datamodel.OsAccount; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Parent class for specific OsAccount event classes. + */ +class OsAccountsEvent extends TskDataModelChangedEvent { + + private static final long serialVersionUID = 1L; + + /** + * Construct a new OsAccountEvent. + * + * @param eventName The name of the event. + * @param account The OsAccount the event applies to. + */ + OsAccountsEvent(String eventName, List accounts) { + super(eventName, accounts, OsAccount::getId); + } + + /** + * Returns the OsAccount that changed. + * + * @return The OsAccount that was changed. + */ + public OsAccount getOsAccount() { + List accounts = getNewValue(); + return accounts.get(0); + } + + @Override + protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + Long id = ids.get(0); + OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id); + List accounts = new ArrayList<>(); + accounts.add(account); + return accounts; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java new file mode 100755 index 0000000000..22ef19584b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java @@ -0,0 +1,41 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.OsAccount; + +/** + * Event published when OS accounts in the case have been updated. + */ +public final class OsAccountsUpdatedEvent extends OsAccountsEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an event published when OS accounts in the case have been + * updated. + * + * @param accounts The accounts. + */ + public OsAccountsUpdatedEvent(List accounts) { + super(Case.Events.OS_ACCOUNT_CHANGED.toString(), accounts); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java new file mode 100755 index 0000000000..cb5308d7ce --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java @@ -0,0 +1,41 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Person; + +/** + * An event fired when persons in a case are updated. + */ +public class PersonsUpdatedEvent extends PersonsEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an event fired when persons in a case are updated. + * + * @param persons The updated persons. + */ + public PersonsUpdatedEvent(List persons) { + super(Case.Events.PERSONS_CHANGED.name(), persons); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java new file mode 100755 index 0000000000..68a47c35c1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java @@ -0,0 +1,165 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * An abstract base class for application events published when Sleuth Kit Data + * Model objects for a case are added, updated, or deleted. + * + * @param A Sleuth Kit Data Model object type. + */ +public abstract class TskDataModelChangedEvent extends AutopsyEvent { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(TskDataModelChangedEvent.class.getName()); + private final boolean isDeletionEvent; + private final List dataModelObjectIds; + private transient List dataModelObjects; + + /** + * Constructs an instance of an abstract base class for application events + * published when Sleuth Kit Data Model objects for a case are added or + * updated. The getNewValue() method of this event will return the objects + * and the getOldValue() method will return an empty list. + * + * @param eventName The event name. + * @param dataModelObjects The Sleuth Kit Data Model objects that have been + * added or updated. + * @param getIdMethod A method that can be applied to the data model + * objects to get their unique numeric IDs (TSK + * object IDs, case database row IDs, etc.). + */ + protected TskDataModelChangedEvent(String eventName, List dataModelObjects, Function getIdMethod) { + super(eventName, null, null); + isDeletionEvent = false; + this.dataModelObjectIds = new ArrayList<>(); + this.dataModelObjectIds.addAll(dataModelObjects.stream().map(o -> getIdMethod.apply(o)).collect(Collectors.toList())); + this.dataModelObjects = new ArrayList<>(); + this.dataModelObjects.addAll(dataModelObjects); + } + + /** + * Constructs an instance of an abstract base class for application events + * published when Sleuth Kit Data Model objects for a case are added or + * updated. The getOldValue() method of this event will return the object + * IDs and the getNewValue() method will return an empty list. + * + * @param eventName The event name. + * @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case + * database row IDs, etc.) of the Sleuth Kit Data + * Model objects that have been deleted. + */ + protected TskDataModelChangedEvent(String eventName, List dataModelObjectIds) { + super(eventName, null, null); + isDeletionEvent = true; + this.dataModelObjectIds = new ArrayList<>(); + this.dataModelObjectIds.addAll(dataModelObjectIds); + dataModelObjects = Collections.emptyList(); + } + + /** + * Gets the the unique numeric IDs (TSK object IDs, case database row IDs, + * etc.) of the Sleuth Kit Data Model objects that were deleted. + * + * @return The unique IDs. + */ + @Override + public List getOldValue() { + if (isDeletionEvent) { + return getDataModelObjectIds(); + } else { + return Collections.emptyList(); + } + } + + /** + * Gets the Sleuth Kit Data Model objects that were added or updated. If + * this event came from another host collaborating on a multi-user case, the + * Sleuth Kit Data Model objects will be reconstructed on the current host. + * + * @return The objects. + */ + @Override + public List getNewValue() { + if (!isDeletionEvent) { + if (dataModelObjects == null) { + try { + Case currentCase = Case.getCurrentCaseThrows(); + SleuthkitCase caseDb = currentCase.getSleuthkitCase(); + dataModelObjects = getDataModelObjects(caseDb, dataModelObjectIds); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error geting TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex); + return Collections.emptyList(); + } + } + return Collections.unmodifiableList(dataModelObjects); + } else { + return Collections.emptyList(); + } + } + + + /** + * Gets the unique numeric IDs (TSK object IDs, case database row IDs, etc.) + * of the Sleuth Kit Data Model objects associated with this application + * event. + * + * This method is provided as an optimization that allows handling of an + * event that came from another host collaborating on a multi-user case + * without reconstructing the data model objects that are the subject s of + * the event. + * + * @return The unique IDs. + */ + public final List getDataModelObjectIds() { + return Collections.unmodifiableList(dataModelObjectIds); + } + + /** + * Gets the Sleuth Kit Data Model objects associated with this application + * event. If this event came from another host collaborating on a multi-user + * case, the Sleuth Kit Data Model objects, this method will be called to + * reconstruct the objects on the current host. + * + * @param caseDb The case database. + * @param ids The unique, numeric IDs (TSK object IDs, case database row + * IDs, etc.) of the Sleuth Kit Data Model objects. + * + * @return The objects. + * + * @throws org.sleuthkit.datamodel.TskCoreException If there is an error + * getting the Sleuth Kit + * Data Model objects. + */ + abstract protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException; + +} From 856eced424d77879b4bd62693e2b29ccafc69946 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 29 Apr 2021 11:55:59 -0400 Subject: [PATCH 03/78] 7553 data model event changes --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 6 ++---- .../autopsy/casemodule/events/TskDataModelChangedEvent.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index f77859d407..b2790bd8ec 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -555,8 +555,7 @@ public class Case { */ @Subscribe public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) { - eventPublisher.publish(new PersonsAddedEvent( - event == null ? Collections.emptyList() : event.getPersons())); + eventPublisher.publish(new PersonsAddedEvent(event.getPersons())); } /** @@ -567,8 +566,7 @@ public class Case { */ @Subscribe public void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event) { - eventPublisher.publish(new PersonsUpdatedEvent( - event == null ? Collections.emptyList() : event.getPersons())); + eventPublisher.publish(new PersonsUpdatedEvent(event.getPersons())); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java index 68a47c35c1..d0d8953e92 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java @@ -148,7 +148,7 @@ public abstract class TskDataModelChangedEvent extends AutopsyEvent { * Gets the Sleuth Kit Data Model objects associated with this application * event. If this event came from another host collaborating on a multi-user * case, the Sleuth Kit Data Model objects, this method will be called to - * reconstruct the objects on the current host. + * reconstruct the objects on the curartifactExists(), I think we should continue to use what we have and suppress the deprecation warnings.Bent host. * * @param caseDb The case database. * @param ids The unique, numeric IDs (TSK object IDs, case database row From bb2e65a770febdbb36e651cfff42b384e5e21ce5 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 4 May 2021 14:05:49 -0400 Subject: [PATCH 04/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/Case.java | 25 ++- .../casemodule/events/HostsAddedEvent.java | 20 +- .../events/HostsAddedToPersonEvent.java | 39 ++++ .../casemodule/events/HostsDeletedEvent.java | 30 +-- .../autopsy/casemodule/events/HostsEvent.java | 46 ++-- .../events/HostsRemovedFromPersonEvent.java | 25 +++ .../casemodule/events/HostsUpdatedEvent.java | 14 +- .../events/OsAccountsAddedEvent.java | 17 +- .../events/OsAccountsDeletedEvent.java | 26 ++- .../casemodule/events/OsAccountsEvent.java | 52 ++--- .../events/OsAccountsUpdatedEvent.java | 14 +- .../casemodule/events/PersonHostsEvent.java | 75 +++++++ .../casemodule/events/PersonsAddedEvent.java | 8 +- .../events/PersonsDeletedEvent.java | 29 +-- .../casemodule/events/PersonsEvent.java | 51 +++-- .../events/PersonsUpdatedEvent.java | 10 +- .../casemodule/events/ReportAddedEvent.java | 25 ++- .../events/TskDataModelChangedEvent.java | 212 +++++++++++------- .../TskDataModelObjectsDeletedEvent.java | 60 +++++ .../datamodel/AutopsyTreeChildFactory.java | 5 +- .../datamodel/DataSourcesByTypeNode.java | 5 +- .../sleuthkit/autopsy/datamodel/HostNode.java | 4 +- .../autopsy/datamodel/OsAccounts.java | 19 +- .../autopsy/datamodel/PersonGroupingNode.java | 9 +- .../hosts/AssociateNewPersonAction.java | 3 +- .../hosts/AssociatePersonAction.java | 3 +- .../hosts/RemoveParentPersonAction.java | 5 +- .../autopsy/events/AutopsyEvent.java | 50 +++-- 28 files changed, 588 insertions(+), 293 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelObjectsDeletedEvent.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b2790bd8ec..06809bffa8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -432,7 +432,7 @@ public class Case { * OSAccount associated with the current case added. Call getOsAccount * to get the added account; */ - OS_ACCOUNT_ADDED, + OS_ACCOUNTS_ADDED, /** * OSAccount associated with the current case has changed. Call * getOsAccount to get the changed account; @@ -441,7 +441,7 @@ public class Case { /** * OSAccount associated with the current case has been deleted. */ - OS_ACCOUNT_REMOVED, + OS_ACCOUNTS_DELETED, /** * Hosts associated with the current case added. */ @@ -449,7 +449,7 @@ public class Case { /** * Hosts associated with the current case has changed. */ - HOSTS_CHANGED, + HOSTS_UPDATED, /** * Hosts associated with the current case has been deleted. */ @@ -461,11 +461,20 @@ public class Case { /** * Persons associated with the current case has changed. */ - PERSONS_CHANGED, + PERSONS_UPDATED, /** * Persons associated with the current case has been deleted. */ - PERSONS_DELETED; + PERSONS_DELETED, + /** + * Hosts have been added to a person. + */ + HOSTS_ADDED_TO_PERSON, + /** + * Hosts have been removed from a person. + */ + HOSTS_REMOVED_FROM_PERSON; + }; /** @@ -511,7 +520,7 @@ public class Case { @Subscribe public void publishOsAccountsDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) { - eventPublisher.publish(new OsAccountsDeletedEvent(event.getObjectIds())); + eventPublisher.publish(new OsAccountsDeletedEvent(event.getOsAccountObjectIds())); } /** @@ -544,7 +553,7 @@ public class Case { */ @Subscribe public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) { - eventPublisher.publish(new HostsDeletedEvent(event.getObjectIds())); + eventPublisher.publish(new HostsDeletedEvent(event.getHostIds())); } /** @@ -577,7 +586,7 @@ public class Case { */ @Subscribe public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) { - eventPublisher.publish(new PersonsDeletedEvent(event.getObjectIds())); + eventPublisher.publish(new PersonsDeletedEvent(event.getPersonIds())); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java index c10b66face..bf355b66b7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java @@ -23,17 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; /** - * Event fired when new hosts are added. + * An application event published when hosts have been added to the Sleuth Kit + * data model for a case. */ -public class HostsAddedEvent extends HostsEvent { - +public final class HostsAddedEvent extends HostsEvent { + private static final long serialVersionUID = 1L; - + /** - * Main constructor. - * @param dataModelObjects The hosts that have been added. + * Constructs an application event published when hosts have been added to + * the Sleuth Kit data model for a case. + * + * @param persons The hosts that have been added. */ - public HostsAddedEvent(List dataModelObjects) { - super(Case.Events.HOSTS_ADDED.name(), dataModelObjects); + public HostsAddedEvent(List hosts) { + super(Case.Events.HOSTS_ADDED.name(), hosts); } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java new file mode 100755 index 0000000000..8b1b9da8a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java @@ -0,0 +1,39 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.Collections; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.Person; + +/** + * + * @author rcordovano + */ +public final class HostsAddedToPersonEvent extends PersonHostsEvent { + + private static final long serialVersionUID = 1L; + + HostsAddedToPersonEvent(Person person, List hosts) { + super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), person, hosts); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java index 3d62b78405..24c2ae091b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsDeletedEvent.java @@ -18,34 +18,34 @@ */ package org.sleuthkit.autopsy.casemodule.events; -import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.Host; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; /** - * Event fired when hosts are deleted. + * Application events published when hosts have been deleted from the Sleuth + * Kit data model for a case. */ -public class HostsDeletedEvent extends TskDataModelChangedEvent { +public final class HostsDeletedEvent extends TskDataModelObjectsDeletedEvent { private static final long serialVersionUID = 1L; /** - * Main constructor. + * Constructs an application event published when hosts have been deleted + * from the Sleuth Kit data model for a case. * - * @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case - * database row IDs, etc.) of the Hosts that have - * been deleted. + * @param hostIds The host IDs of the deleted hosts. */ - public HostsDeletedEvent(List dataModelObjectIds) { - super(Case.Events.HOSTS_DELETED.name(), dataModelObjectIds); + public HostsDeletedEvent(List hostIds) { + super(Case.Events.HOSTS_DELETED.name(), hostIds); } - @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - return Collections.emptyList(); + /** + * Gets the host IDs of the hosts that have been deleted. + * + * @return The host IDs. + */ + public List getHostIds() { + return getOldValue(); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java index 69f68c0b4e..1ac350c272 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java @@ -22,44 +22,48 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.sleuthkit.datamodel.Host; -import org.sleuthkit.datamodel.HostManager; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Base event class for when something pertaining to hosts changes. + * A base class for application events published when hosts in the Sleuth Kit + * data model for a case have been added or updated. */ -public class HostsEvent extends TskDataModelChangedEvent { +public class HostsEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; /** - * Main constructor. + * Constructs the base class part of an application event published when + * hosts in the Sleuth Kit data model for a case have been added or updated. * * @param eventName The name of the Case.Events enum value for the event - * type. - * @param dataModelObjects The list of hosts for the event. + * type. + * @param hosts The hosts. */ - protected HostsEvent(String eventName, List dataModelObjects) { - super(eventName, dataModelObjects, Host::getHostId); + protected HostsEvent(String eventName, List hosts) { + super(eventName, null, null, hosts, Host::getHostId); + } + + /** + * Gets the hosts that have been added or updated. + * + * @return The hosts. + */ + public List getHosts() { + return getNewValue(); } @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - HostManager hostManager = caseDb.getHostManager(); - List toRet = new ArrayList<>(); - if (ids != null) { - for (Long id : ids) { - if (id == null) { - continue; - } - - Optional thisHostOpt = hostManager.getHostById(id); - thisHostOpt.ifPresent((h) -> toRet.add(h)); + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List hosts = new ArrayList<>(); + for (Long id : ids) { + Optional host = caseDb.getHostManager().getHostById(id); + if (host.isPresent()) { + hosts.add(host.get()); } } - - return toRet; + return hosts; } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java new file mode 100755 index 0000000000..eaf200368d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.casemodule.events; + +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.Person; + +/** + * + * @author rcordovano + */ +public class HostsRemovedFromPersonEvent extends PersonHostsEvent { + + private static final long serialVersionUID = 1L; + + HostsRemovedFromPersonEvent(Person person, List hosts) { + super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), person, hosts); + } + +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java index 6b06382c0a..ec00fc5ad6 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsUpdatedEvent.java @@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; /** - * Event fired when hosts are changed. + * Application events published when hosts in the Sleuth Kit data model for + * a case have been updated. */ public class HostsUpdatedEvent extends HostsEvent { private static final long serialVersionUID = 1L; /** - * Main constructor. + * Constructs an application event published when hosts in the Sleuth Kit + * data model for a case have been updated. * - * @param dataModelObjects The new values for the hosts that have been - * changed. + * @param hosts The updated persons. */ - public HostsUpdatedEvent(List dataModelObjects) { - super(Case.Events.HOSTS_CHANGED.name(), dataModelObjects); + public HostsUpdatedEvent(List hosts) { + super(Case.Events.HOSTS_UPDATED.name(), hosts); } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java index 49a722062b..f4273408c6 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsAddedEvent.java @@ -19,22 +19,25 @@ package org.sleuthkit.autopsy.casemodule.events; import java.util.List; -import org.sleuthkit.autopsy.casemodule.Case; +import static org.sleuthkit.autopsy.casemodule.Case.Events.OS_ACCOUNTS_ADDED; import org.sleuthkit.datamodel.OsAccount; /** - * Event published when OS accounts are added to a case. + * An application event published when OS accounts are added to the Sleuth Kit + * data model for a case. */ public final class OsAccountsAddedEvent extends OsAccountsEvent { private static final long serialVersionUID = 1L; - + /** - * Constructs an event published when OS accounts are added to a case. - * @param accounts + * Constructs an application event published when OS accounts are added to + * the Sleuth Kit data model for a case. + * + * @param osAccounts The OS accounts that were added. */ - public OsAccountsAddedEvent(List accounts) { - super(Case.Events.OS_ACCOUNT_ADDED.toString(), accounts); + public OsAccountsAddedEvent(List osAccounts) { + super(OS_ACCOUNTS_ADDED.toString(), osAccounts); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java index 52fe8d49a8..f3f65a0aed 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsDeletedEvent.java @@ -18,32 +18,34 @@ */ package org.sleuthkit.autopsy.casemodule.events; -import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.OsAccount; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; /** - * An event published when OS accounts are deleted from a case. + * An application event published when OS accounts have been deleted from the + * Sleuth Kit data model for a case. */ -public final class OsAccountsDeletedEvent extends TskDataModelChangedEvent { +public final class OsAccountsDeletedEvent extends TskDataModelObjectsDeletedEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event published when OS accounts are deleted from a case. + * Constructs an application event published when OS accounts have been + * deleted from the Sleuth Kit data model for a case. * - * @param osAccountObjectIds The object IDs of the deleted accounts. + * @param osAccountObjectIds TSK object IDs of the deleted accounts. */ public OsAccountsDeletedEvent(List osAccountObjectIds) { - super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectIds); + super(Case.Events.OS_ACCOUNTS_DELETED.toString(), osAccountObjectIds); } - @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - return Collections.emptyList(); + /** + * Gets the Sleuth Kit object IDs of the deleted OS accounts. + * + * @return The object IDs. + */ + List getOsAccountObjectIds() { + return getOldValue(); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java index af214776a8..434d8c8f60 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsEvent.java @@ -20,45 +20,47 @@ package org.sleuthkit.autopsy.casemodule.events; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Parent class for specific OsAccount event classes. + * A base class for application events published when OS accounts in the Sleuth + * Kit data model for a case have been added or updated. */ -class OsAccountsEvent extends TskDataModelChangedEvent { +class OsAccountsEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; - + /** - * Construct a new OsAccountEvent. - * - * @param eventName The name of the event. - * @param account The OsAccount the event applies to. + * Constructs the base class part of an application event published when + * OS accounts in the Sleuth Kit data model for a case have been added or + * updated. + * + * @param eventName The name of the Case.Events enum value for the event + * type. + * @param account The OS accounts. */ - OsAccountsEvent(String eventName, List accounts) { - super(eventName, accounts, OsAccount::getId); + OsAccountsEvent(String eventName, List osAccounts) { + super(eventName, null, null, osAccounts, OsAccount::getId); } - + /** - * Returns the OsAccount that changed. - * - * @return The OsAccount that was changed. + * Gets the OS accounts that have been added or updated. + * + * @return The OS accounts. */ - public OsAccount getOsAccount() { - List accounts = getNewValue(); - return accounts.get(0); + public List getOsAccounts() { + return getNewValue(); } @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - Long id = ids.get(0); - OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id); - List accounts = new ArrayList<>(); - accounts.add(account); - return accounts; - } + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List osAccounts = new ArrayList<>(); + for (Long id : ids) { + osAccounts.add(caseDb.getOsAccountManager().getOsAccountByObjectId(id)); + } + return osAccounts; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java index 22ef19584b..9795f055d6 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java @@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.OsAccount; /** - * Event published when OS accounts in the case have been updated. + * An application event published when OS accounts in the Sleuth Kit data model + * for a case have been updated. */ public final class OsAccountsUpdatedEvent extends OsAccountsEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event published when OS accounts in the case have been - * updated. + * Constructs an application event published when OS accounts in the Sleuth + * Kit data model for a case have been updated. * - * @param accounts The accounts. + * @param osAccounts The OS accounts that were updated. */ - public OsAccountsUpdatedEvent(List accounts) { - super(Case.Events.OS_ACCOUNT_CHANGED.toString(), accounts); + public OsAccountsUpdatedEvent(List osAccounts) { + super(Case.Events.OS_ACCOUNT_CHANGED.toString(), osAccounts); } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java new file mode 100755 index 0000000000..f1426e38e4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java @@ -0,0 +1,75 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.casemodule.events; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.Person; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * @author rcordovano + */ +public abstract class PersonHostsEvent extends TskDataModelChangedEvent { + + private static final long serialVersionUID = 1L; + + /** + * + * @param eventName + * @param person + * @param hosts + */ + PersonHostsEvent(String eventName, Person person, List hosts) { + super(eventName, Collections.singletonList(person), Person::getPersonId, hosts, Host::getHostId); + } + + /** + * + * @return + */ + Person getPerson() { + return getOldValue().get(0); + } + + /** + * + * @return + */ + List getHosts() { + return getNewValue(); + } + + @Override + protected List getOldValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List persons = new ArrayList<>(); + for (Long id : ids) { + Optional person = caseDb.getPersonManager().getPerson(id); + if (person.isPresent()) { + persons.add(person.get()); + } + } + return persons; + } + + @Override + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List hosts = new ArrayList<>(); + for (Long id : ids) { + Optional host = caseDb.getHostManager().getHostById(id); + if (host.isPresent()) { + hosts.add(host.get()); + } + } + return hosts; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java index c61bc177f3..afd101eac3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsAddedEvent.java @@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Person; /** - * Event fired when new persons are added to a case. + * An application event published when persons have been added to the Sleuth Kit + * data model for a case. */ public class PersonsAddedEvent extends PersonsEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event fired when new persons are added to a case. + * Constructs an application event published when persons have been added to + * the Sleuth Kit data model for a case. * * @param persons The persons that have been added. */ public PersonsAddedEvent(List persons) { super(Case.Events.PERSONS_ADDED.name(), persons); } - + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java index db80759214..46c024b7dd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsDeletedEvent.java @@ -18,33 +18,34 @@ */ package org.sleuthkit.autopsy.casemodule.events; -import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.Person; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; /** - * An event fired when persons are deleted from the case. + * Application events published when persons have been deleted from the Sleuth + * Kit data model for a case. */ -public class PersonsDeletedEvent extends TskDataModelChangedEvent { +public class PersonsDeletedEvent extends TskDataModelObjectsDeletedEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event fired when persons are deleted from the case. + * Constructs an application event published when persons have been deleted + * from the Sleuth Kit data model for a case. * - * @param dataModelObjectIds The unique numeric IDs (case database row IDs) - * of the persons that have been deleted. + * @param personIds The IDs of the persons that have been deleted. */ - public PersonsDeletedEvent(List dataModelObjectIds) { - super(Case.Events.PERSONS_DELETED.name(), dataModelObjectIds); + public PersonsDeletedEvent(List personIds) { + super(Case.Events.PERSONS_DELETED.name(), personIds); } - @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - return Collections.emptyList(); + /** + * Gets the person IDs of the persons that have been deleted. + * + * @return The person IDs. + */ + List getPersonIds() { + return getOldValue(); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java index 5380ee4874..2d3b322ea7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsEvent.java @@ -22,44 +22,49 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.sleuthkit.datamodel.Person; -import org.sleuthkit.datamodel.PersonManager; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Base event class for when something pertaining to persons changes. + * A base class for application events published when persons in the Sleuth Kit + * data model for a case have been added or updated. */ -public class PersonsEvent extends TskDataModelChangedEvent { +public class PersonsEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; - + /** - * Main constructor. + * Constructs the base class part of an application event published when + * persons in the Sleuth Kit data model for a case have been added or + * updated. * * @param eventName The name of the Case.Events enum value for the event - * type. - * @param dataModelObjects The list of persons for the event. + * type. + * @param persons The persons. */ - PersonsEvent(String eventName, List dataModelObjects) { - super(eventName, dataModelObjects, Person::getPersonId); + PersonsEvent(String eventName, List persons) { + super(eventName, null, null, persons, Person::getPersonId); } - - @Override - protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - PersonManager personManager = caseDb.getPersonManager(); - List toRet = new ArrayList<>(); - if (ids != null) { - for (Long id : ids) { - if (id == null) { - continue; - } - Optional thisPersonOpt = personManager.getPerson(id); - thisPersonOpt.ifPresent((h) -> toRet.add(h)); + /** + * Gets the persons that have been added or updated. + * + * @return The persons. + */ + public List getPersons() { + return getNewValue(); + } + + @Override + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List persons = new ArrayList<>(); + for (Long id : ids) { + Optional person = caseDb.getPersonManager().getPerson(id); + if (person.isPresent()) { + persons.add(person.get()); } } - - return toRet; + return persons; } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java index cb5308d7ce..572b832688 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonsUpdatedEvent.java @@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Person; /** - * An event fired when persons in a case are updated. + * Application events published when persons in the Sleuth Kit data model for + * a case have been updated. */ public class PersonsUpdatedEvent extends PersonsEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event fired when persons in a case are updated. + * Constructs an application event published when persons in the Sleuth Kit + * data model for a case have been updated. * * @param persons The updated persons. */ public PersonsUpdatedEvent(List persons) { - super(Case.Events.PERSONS_CHANGED.name(), persons); + super(Case.Events.PERSONS_UPDATED.name(), persons); } - + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java index 47c5a30d5d..9da76a4366 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java @@ -28,33 +28,38 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Event published when a report is added to a case. + * An application event published when a report is added to a case. */ -public final class ReportAddedEvent extends TskDataModelChangedEvent { +public final class ReportAddedEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; /** - * Constructs an event published when a report is added to a case. + * Constructs an application event published when a report is added to a + * case. * - * @param report The data source that was added. + * @param report The report that was added. */ public ReportAddedEvent(Report report) { - super(Case.Events.REPORT_ADDED.toString(), Stream.of(report).collect(Collectors.toList()), Report::getId); + super(Case.Events.REPORT_ADDED.toString(), null, null, Stream.of(report).collect(Collectors.toList()), Report::getId); } + /** + * Gets the reoprt that was added to the case. + * + * @return The report. + */ public Report getReport() { List reports = getNewValue(); return reports.get(0); } - + @Override - protected List getDataModelObjects(SleuthkitCase caseD, List ids) throws TskCoreException { + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { Long id = ids.get(0); - Report report = caseD.getReportById(id); List reports = new ArrayList<>(); - reports.add(report); + reports.add(caseDb.getReportById(id)); return reports; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java index d0d8953e92..07ed7166d7 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java @@ -32,123 +32,142 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * An abstract base class for application events published when Sleuth Kit Data - * Model objects for a case are added, updated, or deleted. + * An abstract base class for application events published when one or more + * Sleuth Kit Data Model objects for a case change in some way. * - * @param A Sleuth Kit Data Model object type. + * This class extends AutopsyEvent. The AutopsyEvent class extends + * PropertyChangeEvent to integrate with legacy use of JavaBeans + * PropertyChangeEvents and PropertyChangeListeners as an application event + * publisher-subcriber mechanism. Subclasses need to decide what constitutes + * "old" and "new" objects for them and are encouraged to provide getters for + * these values that do not require clients to cast the return values. + * + * The AutopsyEvent class implements Serializable to allow local event instances + * to be published to other Autopsy nodes over a network in serialized form. TSK + * Data Model objects are generally not serializable because they encapsulate a + * reference to a SleuthkitCase object that represents the case database and + * which has local JDBC Connection objects. For this reason, this class supports + * serialization of the unique numeric IDs (TSK object IDs, case database row + * IDs, etc.) of the subject TSK Data Model objects and the "reconstruction" of + * those objects on other Autopsy nodes by querying the case database by unique + * ID. + * + * @param The Sleuth Kit Data Model object type of the "old" data model + * objects. + * @param The Sleuth Kit Data Model object type of the "new" data model + * objects. */ -public abstract class TskDataModelChangedEvent extends AutopsyEvent { +public abstract class TskDataModelChangedEvent extends AutopsyEvent { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(TskDataModelChangedEvent.class.getName()); - private final boolean isDeletionEvent; - private final List dataModelObjectIds; - private transient List dataModelObjects; + private final boolean hasOldValue; + private final List oldValueIds; + private transient List oldValueObjects; + private final boolean hasNewValue; + private final List newValueIds; + private transient List newValueObjects; /** - * Constructs an instance of an abstract base class for application events - * published when Sleuth Kit Data Model objects for a case are added or - * updated. The getNewValue() method of this event will return the objects - * and the getOldValue() method will return an empty list. + * Constructs the base class part for application events published when one + * or more Sleuth Kit Data Model objects for a case change in some way. * - * @param eventName The event name. - * @param dataModelObjects The Sleuth Kit Data Model objects that have been - * added or updated. - * @param getIdMethod A method that can be applied to the data model - * objects to get their unique numeric IDs (TSK - * object IDs, case database row IDs, etc.). + * @param eventName The event name. + * @param oldValueObjects A list of he Data Model objects that have been + * designated as the "old" objects in the event. + * May be null. + * @param oldValueGetIdMethod A method that can be applied to the "old" data + * model objects to get their unique numeric IDs + * (TSK object IDs, case database row IDs, etc.). + * May be null if there are no "old" objects. + * @param newValueObjects A list of he Data Model objects that have been + * designated as the "new" objects in the event. + * May be null. + * @param newValueGetIdMethod A method that can be applied to the "new" data + * model objects to get their unique numeric IDs + * (TSK object IDs, case database row IDs, etc.). + * May be null if there are no "new" objects. */ - protected TskDataModelChangedEvent(String eventName, List dataModelObjects, Function getIdMethod) { + protected TskDataModelChangedEvent(String eventName, List oldValueObjects, Function oldValueGetIdMethod, List newValueObjects, Function newValueGetIdMethod) { super(eventName, null, null); - isDeletionEvent = false; - this.dataModelObjectIds = new ArrayList<>(); - this.dataModelObjectIds.addAll(dataModelObjects.stream().map(o -> getIdMethod.apply(o)).collect(Collectors.toList())); - this.dataModelObjects = new ArrayList<>(); - this.dataModelObjects.addAll(dataModelObjects); - } - - /** - * Constructs an instance of an abstract base class for application events - * published when Sleuth Kit Data Model objects for a case are added or - * updated. The getOldValue() method of this event will return the object - * IDs and the getNewValue() method will return an empty list. - * - * @param eventName The event name. - * @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case - * database row IDs, etc.) of the Sleuth Kit Data - * Model objects that have been deleted. - */ - protected TskDataModelChangedEvent(String eventName, List dataModelObjectIds) { - super(eventName, null, null); - isDeletionEvent = true; - this.dataModelObjectIds = new ArrayList<>(); - this.dataModelObjectIds.addAll(dataModelObjectIds); - dataModelObjects = Collections.emptyList(); - } - - /** - * Gets the the unique numeric IDs (TSK object IDs, case database row IDs, - * etc.) of the Sleuth Kit Data Model objects that were deleted. - * - * @return The unique IDs. - */ - @Override - public List getOldValue() { - if (isDeletionEvent) { - return getDataModelObjectIds(); + oldValueIds = new ArrayList<>(); + this.oldValueObjects = new ArrayList<>(); + if (oldValueObjects != null) { + hasOldValue = true; + oldValueIds.addAll(oldValueObjects.stream() + .map(o -> oldValueGetIdMethod.apply(o)) + .collect(Collectors.toList())); + this.oldValueObjects.addAll(oldValueObjects); } else { - return Collections.emptyList(); + hasOldValue = false; + } + newValueIds = new ArrayList<>(); + this.newValueObjects = new ArrayList<>(); + if (oldValueObjects != null) { + hasNewValue = true; + newValueIds.addAll(newValueObjects.stream() + .map(o -> newValueGetIdMethod.apply(o)) + .collect(Collectors.toList())); + this.newValueObjects.addAll(newValueObjects); + } else { + hasNewValue = false; } } /** - * Gets the Sleuth Kit Data Model objects that were added or updated. If - * this event came from another host collaborating on a multi-user case, the - * Sleuth Kit Data Model objects will be reconstructed on the current host. + * Gets a list of the Data Model objects that have been designated as the + * "old" objects in the event. * - * @return The objects. + * @return The list of the "old" data model objects. May be empty. */ @Override - public List getNewValue() { - if (!isDeletionEvent) { - if (dataModelObjects == null) { + public List getOldValue() { + if (hasOldValue) { + if (oldValueObjects == null) { try { Case currentCase = Case.getCurrentCaseThrows(); SleuthkitCase caseDb = currentCase.getSleuthkitCase(); - dataModelObjects = getDataModelObjects(caseDb, dataModelObjectIds); + oldValueObjects = getOldValueObjects(caseDb, oldValueIds); } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error geting TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex); + logger.log(Level.SEVERE, String.format("Error getting oldValue() TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex); return Collections.emptyList(); } } - return Collections.unmodifiableList(dataModelObjects); + return Collections.unmodifiableList(oldValueObjects); } else { return Collections.emptyList(); } } + /** + * Gets a list of the Data Model objects that have been designated as the + * "new" objects in the event. + * + * @return The list of the "new" data model objects. May be empty. + */ + @Override + public List getNewValue() { + if (hasNewValue) { + if (newValueObjects == null) { + try { + Case currentCase = Case.getCurrentCaseThrows(); + SleuthkitCase caseDb = currentCase.getSleuthkitCase(); + newValueObjects = getNewValueObjects(caseDb, newValueIds); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error getting newValue() TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex); + return Collections.emptyList(); + } + } + return Collections.unmodifiableList(newValueObjects); + } else { + return Collections.emptyList(); + } + } /** - * Gets the unique numeric IDs (TSK object IDs, case database row IDs, etc.) - * of the Sleuth Kit Data Model objects associated with this application - * event. - * - * This method is provided as an optimization that allows handling of an - * event that came from another host collaborating on a multi-user case - * without reconstructing the data model objects that are the subject s of - * the event. - * - * @return The unique IDs. - */ - public final List getDataModelObjectIds() { - return Collections.unmodifiableList(dataModelObjectIds); - } - - /** - * Gets the Sleuth Kit Data Model objects associated with this application - * event. If this event came from another host collaborating on a multi-user - * case, the Sleuth Kit Data Model objects, this method will be called to - * reconstruct the objects on the curartifactExists(), I think we should continue to use what we have and suppress the deprecation warnings.Bent host. + * Reconstructs the "old" Sleuth Kit Data Model objects associated with this + * application event, if any, using the given unique numeric IDs (TSK object + * IDs, case database row IDs, etc.) to query the given case database. * * @param caseDb The case database. * @param ids The unique, numeric IDs (TSK object IDs, case database row @@ -160,6 +179,27 @@ public abstract class TskDataModelChangedEvent extends AutopsyEvent { * getting the Sleuth Kit * Data Model objects. */ - abstract protected List getDataModelObjects(SleuthkitCase caseDb, List ids) throws TskCoreException; + protected List getOldValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return Collections.emptyList(); + } + + /** + * Reconstructs the "new" Sleuth Kit Data Model objects associated with this + * application event, if any, using the given unique numeric IDs (TSK object + * IDs, case database row IDs, etc.) to query the given case database. + * + * @param caseDb The case database. + * @param ids The unique, numeric IDs (TSK object IDs, case database row + * IDs, etc.) of the Sleuth Kit Data Model objects. + * + * @return The objects. + * + * @throws org.sleuthkit.datamodel.TskCoreException If there is an error + * getting the Sleuth Kit + * Data Model objects. + */ + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return Collections.emptyList(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelObjectsDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelObjectsDeletedEvent.java new file mode 100755 index 0000000000..7e5929e4a1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelObjectsDeletedEvent.java @@ -0,0 +1,60 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +/** + * An abstract base class for application events published when one or more + * Sleuth Kit Data Model objects for a case have been deleted. + * + * This class extends AutopsyEvent. The AutopsyEvent class extends + * PropertyChangeEvent to integrate with legacy use of JavaBeans + * PropertyChangeEvents and PropertyChangeListeners as an application event + * publisher-subcriber mechanism. Subclasses need to decide what constitutes + * "old" and "new" objects for them. + * + * For this class the "old" values are the unique numeric IDs (TSK object IDs, + * case database row IDs, etc.) of the deleted TSK Data Model objects. There are + * no "new" values. Subclasses are encouraged to provide less generic getters + * with descriptive names for the unique IDs than the override of the inherited + * getOldValue() method below. These getters can be implemented by delegating to + * getOldValue(). + */ +public class TskDataModelObjectsDeletedEvent extends AutopsyEvent { + + private static final long serialVersionUID = 1L; + + private final List deletedObjectIds; + + protected TskDataModelObjectsDeletedEvent(String eventName, List deletedObjectIds) { + super(eventName, null, null); + this.deletedObjectIds = new ArrayList<>(); + this.deletedObjectIds.addAll(deletedObjectIds); + } + + @Override + public List getOldValue() { + return Collections.unmodifiableList(deletedObjectIds); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java index 22b340d5f1..ca4368e1e4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java @@ -47,13 +47,12 @@ import org.sleuthkit.datamodel.TskCoreException; */ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable { - private static final Set LISTENING_EVENTS = EnumSet.of( - Case.Events.DATA_SOURCE_ADDED, + private static final Set LISTENING_EVENTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_ADDED, Case.Events.HOSTS_DELETED, Case.Events.PERSONS_ADDED, Case.Events.PERSONS_DELETED, - Case.Events.PERSONS_CHANGED + Case.Events.PERSONS_UPDATED ); private static final Set LISTENING_EVENT_NAMES = LISTENING_EVENTS.stream() diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java index 1fab7774c7..700875d161 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java @@ -50,11 +50,10 @@ public class DataSourcesByTypeNode extends DisplayableItemNode { */ public static class DataSourcesByTypeChildren extends ChildFactory.Detachable { - private static final Set UPDATE_EVTS = EnumSet.of( - Case.Events.DATA_SOURCE_ADDED, + private static final Set UPDATE_EVTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_ADDED, Case.Events.HOSTS_DELETED, - Case.Events.HOSTS_CHANGED); + Case.Events.HOSTS_UPDATED); private static final Set UPDATE_EVT_STRS = UPDATE_EVTS.stream() .map(evt -> evt.name()) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java index 57253ef60e..644e2b6b80 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java @@ -177,7 +177,7 @@ public class HostNode extends DisplayableItemNode { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsUpdatedEvent) { + if (hostId != null && eventType.equals(Case.Events.HOSTS_UPDATED.toString()) && evt instanceof HostsUpdatedEvent) { ((HostsUpdatedEvent) evt).getNewValue().stream() .filter(h -> h != null && h.getHostId() == hostId) .findFirst() @@ -246,7 +246,7 @@ public class HostNode extends DisplayableItemNode { host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName)); hostId = host == null ? null : host.getHostId(); - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED), + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_UPDATED), WeakListeners.propertyChange(hostChangePcl, this)); super.setName(displayName); super.setDisplayName(displayName); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 42a69caa99..9472a65616 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -124,8 +124,8 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString()) - || eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) { + if (eventType.equals(Case.Events.OS_ACCOUNTS_ADDED.toString()) + || eventType.equals(Case.Events.OS_ACCOUNTS_DELETED.toString())) { refresh(true); } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { // case was closed. Remove listeners so that we don't get called with a stale case handle @@ -139,13 +139,13 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override protected void addNotify() { - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNTS_ADDED, Case.Events.OS_ACCOUNTS_DELETED), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); } @Override protected void removeNotify() { - Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener); + Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_ADDED), listener); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); } @@ -184,10 +184,13 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) { - if (((OsAccountsUpdatedEvent) evt).getOsAccount().getId() == account.getId()) { - // Update the account node to the new one - account = ((OsAccountsUpdatedEvent) evt).getOsAccount(); - updateSheet(); + OsAccountsUpdatedEvent updateEvent = (OsAccountsUpdatedEvent) evt; + for (OsAccount acct : updateEvent.getOsAccounts()) { + if (acct.getId() == account.getId()) { + account = acct; + updateSheet(); + break; + } } } else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) { OsAccountRealm realm = (OsAccountRealm) evt.getNewValue(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java index 3934ffa06c..736cab3028 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java @@ -68,10 +68,9 @@ public class PersonGroupingNode extends DisplayableItemNode { private static final Logger logger = Logger.getLogger(PersonChildren.class.getName()); - private static final Set CHILD_EVENTS = EnumSet.of( - Case.Events.HOSTS_ADDED, + private static final Set CHILD_EVENTS = EnumSet.of(Case.Events.HOSTS_ADDED, Case.Events.HOSTS_DELETED, - Case.Events.PERSONS_CHANGED); + Case.Events.PERSONS_UPDATED); private static final Set CHILD_EVENTS_STR = CHILD_EVENTS.stream() .map(ev -> ev.name()) @@ -145,7 +144,7 @@ public class PersonGroupingNode extends DisplayableItemNode { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsUpdatedEvent) { + if (personId != null && eventType.equals(Case.Events.PERSONS_UPDATED.toString()) && evt instanceof PersonsUpdatedEvent) { ((PersonsUpdatedEvent) evt).getNewValue().stream() .filter(p -> p != null && p.getPersonId() == personId) .findFirst() @@ -192,7 +191,7 @@ public class PersonGroupingNode extends DisplayableItemNode { this.setIconBaseWithExtension(ICON_PATH); this.person = person; this.personId = person == null ? null : person.getPersonId(); - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED), + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_UPDATED), WeakListeners.propertyChange(personChangePcl, this)); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociateNewPersonAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociateNewPersonAction.java index 2cb89995c6..ff86ea65db 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociateNewPersonAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociateNewPersonAction.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel.hosts; import java.awt.Frame; import java.awt.event.ActionEvent; +import java.util.Collections; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.JOptionPane; @@ -66,7 +67,7 @@ public class AssociateNewPersonAction extends AbstractAction { newPersonName = getAddDialogName(); if (StringUtils.isNotBlank(newPersonName)) { Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName); - Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person); + Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().addHostsToPerson(person, Collections.singletonList(host)); } } catch (NoCurrentCaseException | TskCoreException ex) { String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociatePersonAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociatePersonAction.java index 70b31a1ac7..e9ca92791e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociatePersonAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/AssociatePersonAction.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.datamodel.hosts; import java.awt.event.ActionEvent; +import java.util.Collections; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.JOptionPane; @@ -65,7 +66,7 @@ public class AssociatePersonAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { try { - Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person); + Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().addHostsToPerson(person, Collections.singletonList(host)); } catch (NoCurrentCaseException | TskCoreException ex) { String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName(); String personName = this.person == null || this.person.getName() == null ? "" : this.person.getName(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/RemoveParentPersonAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/RemoveParentPersonAction.java index c8ae974bb0..f83d3bd874 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/hosts/RemoveParentPersonAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/hosts/RemoveParentPersonAction.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.datamodel.hosts; import java.awt.event.ActionEvent; +import java.util.Collections; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.JOptionPane; @@ -46,6 +47,7 @@ public class RemoveParentPersonAction extends AbstractAction { private static final Logger logger = Logger.getLogger(RemoveParentPersonAction.class.getName()); + private final Person person; private final Host host; /** @@ -59,12 +61,13 @@ public class RemoveParentPersonAction extends AbstractAction { person == null || person.getName() == null ? Bundle.RemoveParentPersonAction_unknownPerson() : person.getName())); this.host = host; + this.person = person; } @Override public void actionPerformed(ActionEvent e) { try { - Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, null); + Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().removeHostsFromPerson(person, Collections.singletonList(host)); } catch (NoCurrentCaseException | TskCoreException ex) { String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName(); logger.log(Level.WARNING, String.format("Unable to remove parent from host: %s", hostName), ex); diff --git a/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java b/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java index c329a03549..7c3a16e5a3 100644 --- a/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java +++ b/Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,12 +22,18 @@ import java.beans.PropertyChangeEvent; import java.io.Serializable; /** - * A base class for events to be published to registered subscribers on both - * this Autopsy node and other Autopsy nodes. The class extends - * PropertyChangeEvent to integrate with legacy use of JavaBeans - * PropertyChangeEvents and PropertyChangeListeners as an application event - * system, and implements Serializable to allow it to be published over a - * network in serialized form. + * A base class for application events that can be published to registered + * subscribers on both this Autopsy node and other Autopsy nodes. + * + * The class extends PropertyChangeEvent to integrate with legacy use of + * JavaBeans PropertyChangeEvents and PropertyChangeListeners as an application + * event publisher-subcriber mechanism. Subclasses need to decide what + * constitutes "old" and "new" objects for them and are encouraged to provide + * getters for these values that do not require clients to cast the return + * values. + * + * This class implements Serializable to allow it to be published over a network + * in serialized form. */ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { @@ -36,17 +42,22 @@ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { /** * Events have a source field set to local or remote to allow event - * subscribers to filter events by source type. + * subscribers to filter events by source type. For a multi-user case, a + * local event has happened on this Autopsy node, and a remote event has + * happened on another Autopsy node. + * + * Events are local by default and are changed to remote events by the event + * publishers on other Autopsy nodes upon event receipt. */ public enum SourceType { - LOCAL, REMOTE }; /** - * Constructs an event that can be published to registered subscribers on - * both this Autopsy node and other Autopsy nodes. + * Constructs the base class part of an application event that can be + * published to registered subscribers on both this Autopsy node and other + * Autopsy nodes. * * @param eventName The event name. * @param oldValue The "old" value to associate with the event. May be @@ -60,7 +71,7 @@ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { } /** - * Gets the source type (local or remote). + * Gets the event source type (local or remote). * * @return SourceType The source type of the event, local or remote. */ @@ -69,12 +80,9 @@ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { } /** - * Gets the source type (local or remote) as a string. This is for clients - * that do not have access to the AutopsyEvent type, and is necessary - * because the events package is not currently a public package within the - * Autopsy-Core NetBeans Module (NBM). + * Gets the event source type (local or remote) as a string. * - * @return A string, either "LOCAL" or "REMOTE", as an Object. + * @return A string, either "LOCAL" or "REMOTE." */ @Override public Object getSource() { @@ -82,10 +90,10 @@ public class AutopsyEvent extends PropertyChangeEvent implements Serializable { } /** - * Sets the source type (local or remote). This field is mutable in this way - * to allow an event to be published both locally and remotely without - * requiring the construction of two separate objects. It is for use by the - * event publishing classes within this package only. + * Sets the source type (local or remote). This field is mutable to allow an + * event to be published both locally and remotely without requiring the + * construction of two separate objects. It is for use by the event + * publishing classes within this package only. * * @param sourceType The source type of the event, local or remote. */ From 0ee586196f5fd7de832f7a2df0322d05d629d550 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 4 May 2021 14:24:24 -0400 Subject: [PATCH 05/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/Case.java | 26 +++++++++---------- .../events/OsAccountsUpdatedEvent.java | 2 +- .../autopsy/datamodel/OsAccounts.java | 4 +-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 06809bffa8..f18c600942 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -429,49 +429,47 @@ public class Case { */ CR_COMMENT_CHANGED, /** - * OSAccount associated with the current case added. Call getOsAccount - * to get the added account; + * One or more OS accounts have been added to the case. */ OS_ACCOUNTS_ADDED, /** - * OSAccount associated with the current case has changed. Call - * getOsAccount to get the changed account; + * One or more OS accounts in the case have been updated. */ - OS_ACCOUNT_CHANGED, + OS_ACCOUNTS_UPDATED, /** - * OSAccount associated with the current case has been deleted. + * One or more OS accounts have been deleted from the case. */ OS_ACCOUNTS_DELETED, /** - * Hosts associated with the current case added. + * One or more hosts have been added to the case. */ HOSTS_ADDED, /** - * Hosts associated with the current case has changed. + * One or more hosts in the case have been updated. */ HOSTS_UPDATED, /** - * Hosts associated with the current case has been deleted. + * One or more hosts have been deleted from the case. */ HOSTS_DELETED, /** - * Persons associated with the current case added. + * One or more persons have been added to the case. */ PERSONS_ADDED, /** - * Persons associated with the current case has changed. + * One or more persons in the case have been updated. */ PERSONS_UPDATED, /** - * Persons associated with the current case has been deleted. + * One or more hosts persons been deleted from the case. */ PERSONS_DELETED, /** - * Hosts have been added to a person. + * One or more hosts have been added to a person. */ HOSTS_ADDED_TO_PERSON, /** - * Hosts have been removed from a person. + * One or more hosts have been removed from a person. */ HOSTS_REMOVED_FROM_PERSON; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java index 9795f055d6..65767ccdfc 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/OsAccountsUpdatedEvent.java @@ -37,7 +37,7 @@ public final class OsAccountsUpdatedEvent extends OsAccountsEvent { * @param osAccounts The OS accounts that were updated. */ public OsAccountsUpdatedEvent(List osAccounts) { - super(Case.Events.OS_ACCOUNT_CHANGED.toString(), osAccounts); + super(Case.Events.OS_ACCOUNTS_UPDATED.toString(), osAccounts); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 9472a65616..322ac8cddb 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -183,7 +183,7 @@ public final class OsAccounts implements AutopsyVisitableItem { private final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) { + if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNTS_UPDATED.name())) { OsAccountsUpdatedEvent updateEvent = (OsAccountsUpdatedEvent) evt; for (OsAccount acct : updateEvent.getOsAccounts()) { if (acct.getId() == account.getId()) { @@ -224,7 +224,7 @@ public final class OsAccounts implements AutopsyVisitableItem { setDisplayName(account.getName()); setIconBaseWithExtension(ICON_PATH); - Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener); + Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_UPDATED), weakListener); } @Override From 92cba46d6d0ed26d2f40498ea79c93a6bdb3b8da Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 4 May 2021 15:07:51 -0400 Subject: [PATCH 06/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/Case.java | 13 ++++++ .../events/HostsAddedToPersonEvent.java | 18 +++++--- .../events/HostsRemovedFromPersonEvent.java | 40 ++++++++++++---- .../casemodule/events/PersonHostsEvent.java | 46 ++++++++++--------- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index f18c600942..0274dd7c23 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -84,8 +84,10 @@ import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.HostsAddedToPersonEvent; import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.HostsRemovedFromPersonEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountsAddedEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountsDeletedEvent; @@ -586,6 +588,17 @@ public class Case { public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) { eventPublisher.publish(new PersonsDeletedEvent(event.getPersonIds())); } + + @Subscribe + public void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event) { + eventPublisher.publish(new HostsAddedToPersonEvent(event.getPerson(), event.getHosts())); + } + + @Subscribe + public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) { + eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHosts())); + } + } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java index 8b1b9da8a3..a7937330d2 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java @@ -18,22 +18,28 @@ */ package org.sleuthkit.autopsy.casemodule.events; -import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Person; /** - * - * @author rcordovano + * Application events published when one or more hosts have been added to a + * person. */ public final class HostsAddedToPersonEvent extends PersonHostsEvent { - + private static final long serialVersionUID = 1L; - + + /** + * Constructs an application event published when one or more hosts have + * been added to a person. + * + * @param person The person. + * @param hosts The hosts. + */ HostsAddedToPersonEvent(Person person, List hosts) { super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), person, hosts); } - + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java index eaf200368d..df84e0bbaa 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2021 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.casemodule.events; @@ -11,15 +24,22 @@ import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Person; /** - * - * @author rcordovano + * Application events published when one or more hosts have been removed from a + * person. */ -public class HostsRemovedFromPersonEvent extends PersonHostsEvent { - +public class HostsRemovedFromPersonEvent extends PersonHostsEvent { + private static final long serialVersionUID = 1L; - + + /** + * Constructs an application event published when one or more hosts have + * been removed from a person. + * + * @param person The person. + * @param hosts The hosts. + */ HostsRemovedFromPersonEvent(Person person, List hosts) { super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), person, hosts); } - -} \ No newline at end of file + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java index f1426e38e4..edf0f8b65d 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java @@ -15,37 +15,41 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * - * @author rcordovano + * An abstract super class for person and host association change events. */ public abstract class PersonHostsEvent extends TskDataModelChangedEvent { - + private static final long serialVersionUID = 1L; - + /** - * - * @param eventName - * @param person - * @param hosts + * Constructs the abstract super class part of a person and host association + * change event. + * + * @param eventName The name of the Case.Events enum value for the event + * type. + * @param person The person that is the subject of the event. + * @param hosts The hosts that are the subject of the event. */ PersonHostsEvent(String eventName, Person person, List hosts) { super(eventName, Collections.singletonList(person), Person::getPersonId, hosts, Host::getHostId); } - + /** - * - * @return + * Gets the person. + * + * @return The person. */ - Person getPerson() { + public Person getPerson() { return getOldValue().get(0); } - + /** - * - * @return + * Gets the hosts. + * + * @return The hosts. */ - List getHosts() { - return getNewValue(); + public List getHosts() { + return getNewValue(); } @Override @@ -58,8 +62,8 @@ public abstract class PersonHostsEvent extends TskDataModelChangedEvent getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { List hosts = new ArrayList<>(); @@ -70,6 +74,6 @@ public abstract class PersonHostsEvent extends TskDataModelChangedEvent Date: Tue, 4 May 2021 15:21:57 -0400 Subject: [PATCH 07/78] 7553 data model event changes --- .../autopsy/casemodule/events/HostsAddedToPersonEvent.java | 2 +- .../autopsy/casemodule/events/HostsRemovedFromPersonEvent.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java index a7937330d2..7bee69fd24 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java @@ -38,7 +38,7 @@ public final class HostsAddedToPersonEvent extends PersonHostsEvent { * @param person The person. * @param hosts The hosts. */ - HostsAddedToPersonEvent(Person person, List hosts) { + public HostsAddedToPersonEvent(Person person, List hosts) { super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), person, hosts); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java index df84e0bbaa..2d51cbba5b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java @@ -38,7 +38,7 @@ public class HostsRemovedFromPersonEvent extends PersonHostsEvent { * @param person The person. * @param hosts The hosts. */ - HostsRemovedFromPersonEvent(Person person, List hosts) { + public HostsRemovedFromPersonEvent(Person person, List hosts) { super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), person, hosts); } From 98ea5d2bc967aa0efd12f49018e85e5f0d64c4f2 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 7 May 2021 12:06:27 -0400 Subject: [PATCH 08/78] 7553 data model event changes --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- .../sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java | 4 ++-- .../org/sleuthkit/autopsy/casemodule/events/HostsEvent.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 0274dd7c23..b477b11ea4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -463,7 +463,7 @@ public class Case { */ PERSONS_UPDATED, /** - * One or more hosts persons been deleted from the case. + * One or more persons been deleted from the case. */ PERSONS_DELETED, /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java index bf355b66b7..b598c375d6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java @@ -23,7 +23,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; /** - * An application event published when hosts have been added to the Sleuth Kit + * Application events published when hosts have been added to the Sleuth Kit * data model for a case. */ public final class HostsAddedEvent extends HostsEvent { @@ -39,5 +39,5 @@ public final class HostsAddedEvent extends HostsEvent { public HostsAddedEvent(List hosts) { super(Case.Events.HOSTS_ADDED.name(), hosts); } - + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java index 1ac350c272..465d265083 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsEvent.java @@ -41,7 +41,7 @@ public class HostsEvent extends TskDataModelChangedEvent { * type. * @param hosts The hosts. */ - protected HostsEvent(String eventName, List hosts) { + HostsEvent(String eventName, List hosts) { super(eventName, null, null, hosts, Host::getHostId); } From 90fc3c7153f0d15321bb5cb73aa9af5a552e83f8 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 10 May 2021 10:29:35 -0400 Subject: [PATCH 09/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java | 2 +- .../autopsy/casemodule/events/TskDataModelChangedEvent.java | 2 +- Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java index b598c375d6..f188c7d5a3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedEvent.java @@ -34,7 +34,7 @@ public final class HostsAddedEvent extends HostsEvent { * Constructs an application event published when hosts have been added to * the Sleuth Kit data model for a case. * - * @param persons The hosts that have been added. + * @param hosts The hosts that have been added. */ public HostsAddedEvent(List hosts) { super(Case.Events.HOSTS_ADDED.name(), hosts); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java index 07ed7166d7..83ced035fc 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TskDataModelChangedEvent.java @@ -103,7 +103,7 @@ public abstract class TskDataModelChangedEvent extends AutopsyEvent { } newValueIds = new ArrayList<>(); this.newValueObjects = new ArrayList<>(); - if (oldValueObjects != null) { + if (newValueObjects != null) { hasNewValue = true; newValueIds.addAll(newValueObjects.stream() .map(o -> newValueGetIdMethod.apply(o)) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java index 644e2b6b80..99b0461112 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HostNode.java @@ -178,7 +178,7 @@ public class HostNode extends DisplayableItemNode { public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); if (hostId != null && eventType.equals(Case.Events.HOSTS_UPDATED.toString()) && evt instanceof HostsUpdatedEvent) { - ((HostsUpdatedEvent) evt).getNewValue().stream() + ((HostsUpdatedEvent) evt).getHosts().stream() .filter(h -> h != null && h.getHostId() == hostId) .findFirst() .ifPresent((newHost) -> { From 2e1cd9d4b1913b664443066a3a658e59891e6367 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 11 May 2021 09:38:18 -0400 Subject: [PATCH 10/78] 7553 data model event changes --- .../org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java index 736cab3028..eccf0eb18a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java @@ -69,8 +69,11 @@ public class PersonGroupingNode extends DisplayableItemNode { private static final Logger logger = Logger.getLogger(PersonChildren.class.getName()); private static final Set CHILD_EVENTS = EnumSet.of(Case.Events.HOSTS_ADDED, + Case.Events.HOSTS_ADDED, Case.Events.HOSTS_DELETED, - Case.Events.PERSONS_UPDATED); + Case.Events.PERSONS_UPDATED, + Case.Events.HOSTS_ADDED_TO_PERSON, + Case.Events.HOSTS_REMOVED_FROM_PERSON); private static final Set CHILD_EVENTS_STR = CHILD_EVENTS.stream() .map(ev -> ev.name()) From 6955b075e10cee9bbf7e83ab2c2c752101a4c54f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 12 May 2021 09:40:00 -0400 Subject: [PATCH 11/78] 7553 data model event changes --- .../sleuthkit/autopsy/casemodule/Case.java | 2 +- .../events/HostsAddedToPersonEvent.java | 51 ++++++++- .../events/HostsRemovedFromPersonEvent.java | 48 ++++++++- .../casemodule/events/PersonHostsEvent.java | 79 -------------- .../datamodel/AutopsyTreeChildFactory.java | 102 +++++++++++------- .../autopsy/datamodel/DataSourcesNode.java | 2 +- .../datamodel/DisplayableItemNodeVisitor.java | 4 +- ...ersonGroupingNode.java => PersonNode.java} | 57 ++++++---- .../datamodel/RootContentChildren.java | 2 +- .../DirectoryTreeTopComponent.java | 6 +- .../directorytree/ViewContextAction.java | 4 +- 11 files changed, 201 insertions(+), 156 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java rename Core/src/org/sleuthkit/autopsy/datamodel/{PersonGroupingNode.java => PersonNode.java} (81%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b477b11ea4..1d0e0dd833 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -596,7 +596,7 @@ public class Case { @Subscribe public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) { - eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHosts())); + eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHostIds())); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java index 7bee69fd24..fffb39d82c 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsAddedToPersonEvent.java @@ -18,16 +18,21 @@ */ package org.sleuthkit.autopsy.casemodule.events; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Person; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Application events published when one or more hosts have been added to a * person. */ -public final class HostsAddedToPersonEvent extends PersonHostsEvent { +public final class HostsAddedToPersonEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; @@ -39,7 +44,49 @@ public final class HostsAddedToPersonEvent extends PersonHostsEvent { * @param hosts The hosts. */ public HostsAddedToPersonEvent(Person person, List hosts) { - super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), person, hosts); + super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), Collections.singletonList(person), Person::getPersonId, hosts, Host::getHostId); } + /** + * Gets the person. + * + * @return The person. + */ + public Person getPerson() { + return getOldValue().get(0); + } + + /** + * Gets the hosts. + * + * @return The hosts. + */ + public List getHosts() { + return getNewValue(); + } + + @Override + protected List getOldValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List persons = new ArrayList<>(); + for (Long id : ids) { + Optional person = caseDb.getPersonManager().getPerson(id); + if (person.isPresent()) { + persons.add(person.get()); + } + } + return persons; + } + + @Override + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List hosts = new ArrayList<>(); + for (Long id : ids) { + Optional host = caseDb.getHostManager().getHostById(id); + if (host.isPresent()) { + hosts.add(host.get()); + } + } + return hosts; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java index 2d51cbba5b..e23ef786ee 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/HostsRemovedFromPersonEvent.java @@ -18,16 +18,21 @@ */ package org.sleuthkit.autopsy.casemodule.events; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Person; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Application events published when one or more hosts have been removed from a * person. */ -public class HostsRemovedFromPersonEvent extends PersonHostsEvent { +public class HostsRemovedFromPersonEvent extends TskDataModelChangedEvent { private static final long serialVersionUID = 1L; @@ -36,10 +41,45 @@ public class HostsRemovedFromPersonEvent extends PersonHostsEvent { * been removed from a person. * * @param person The person. - * @param hosts The hosts. + * @param hostIds The host IDs of the removed hosts. */ - public HostsRemovedFromPersonEvent(Person person, List hosts) { - super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), person, hosts); + public HostsRemovedFromPersonEvent(Person person, List hostIds) { + super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), Collections.singletonList(person), Person::getPersonId, hostIds, (id -> id)); + } + + /** + * Gets the person. + * + * @return The person. + */ + public Person getPerson() { + return getOldValue().get(0); + } + + /** + * Gets the host IDs of the removed hosts. + * + * @return The host IDs. + */ + public List getHostIds() { + return getNewValue(); + } + + @Override + protected List getOldValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + List persons = new ArrayList<>(); + for (Long id : ids) { + Optional person = caseDb.getPersonManager().getPerson(id); + if (person.isPresent()) { + persons.add(person.get()); + } + } + return persons; + } + + @Override + protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { + return ids; } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java deleted file mode 100755 index edf0f8b65d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/PersonHostsEvent.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.casemodule.events; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import org.sleuthkit.datamodel.Host; -import org.sleuthkit.datamodel.Person; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * An abstract super class for person and host association change events. - */ -public abstract class PersonHostsEvent extends TskDataModelChangedEvent { - - private static final long serialVersionUID = 1L; - - /** - * Constructs the abstract super class part of a person and host association - * change event. - * - * @param eventName The name of the Case.Events enum value for the event - * type. - * @param person The person that is the subject of the event. - * @param hosts The hosts that are the subject of the event. - */ - PersonHostsEvent(String eventName, Person person, List hosts) { - super(eventName, Collections.singletonList(person), Person::getPersonId, hosts, Host::getHostId); - } - - /** - * Gets the person. - * - * @return The person. - */ - public Person getPerson() { - return getOldValue().get(0); - } - - /** - * Gets the hosts. - * - * @return The hosts. - */ - public List getHosts() { - return getNewValue(); - } - - @Override - protected List getOldValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - List persons = new ArrayList<>(); - for (Long id : ids) { - Optional person = caseDb.getPersonManager().getPerson(id); - if (person.isPresent()) { - persons.add(person.get()); - } - } - return persons; - } - - @Override - protected List getNewValueObjects(SleuthkitCase caseDb, List ids) throws TskCoreException { - List hosts = new ArrayList<>(); - for (Long id : ids) { - Optional host = caseDb.getHostManager().getHostById(id); - if (host.isPresent()) { - hosts.add(host.get()); - } - } - return hosts; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java index ca4368e1e4..387b7d24e1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. + * + * Copyright 2018-2021 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. @@ -42,33 +42,40 @@ import org.sleuthkit.datamodel.SleuthkitVisitableItem; import org.sleuthkit.datamodel.TskCoreException; /** - * Child factory to create the top level children of the autopsy tree - * + * A child factory to create the top level nodes in the main tree view. These + * nodes are the child nodes of the invisible root node of the tree. The child + * nodes that are created vary with the view option selected by the user: group + * by data type or group by person/host. */ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable { - private static final Set LISTENING_EVENTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, + private static final Set EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_ADDED, Case.Events.HOSTS_DELETED, Case.Events.PERSONS_ADDED, Case.Events.PERSONS_DELETED, - Case.Events.PERSONS_UPDATED + Case.Events.HOSTS_ADDED_TO_PERSON, + Case.Events.HOSTS_REMOVED_FROM_PERSON ); - private static final Set LISTENING_EVENT_NAMES = LISTENING_EVENTS.stream() + private static final Set EVENTS_OF_INTEREST_NAMES = EVENTS_OF_INTEREST.stream() .map(evt -> evt.name()) .collect(Collectors.toSet()); private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName()); /** - * Listener for handling DATA_SOURCE_ADDED events. + * Listener for application events published when persons and/or hosts are + * added to or deleted from the data model for the current case. If the user + * has selected the group by person/host option for the tree, these events + * mean that the top-level person/host nodes in the tree need to be + * refreshed to reflect the changes. */ private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (LISTENING_EVENT_NAMES.contains(eventType) + if (EVENTS_OF_INTEREST_NAMES.contains(eventType) && Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { refreshChildren(); } @@ -78,26 +85,38 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable list) { + protected boolean createKeys(List keys) { try { SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { + /* + * The user has selected the group by person/host tree view + * option. + */ PersonManager personManager = tskCase.getPersonManager(); List persons = personManager.getPersons(); // show persons level if there are persons to be shown @@ -105,10 +124,10 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable keys = new ArrayList<>(Arrays.asList( + /* + * The user has selected the group by data type tree view + * option. + */ + List groupByDataTypeKeys = new ArrayList<>(Arrays.asList( new DataSourcesByType(), new Views(tskCase), new Results(tskCase), new Tags(), new Reports())); - - list.addAll(keys); + keys.addAll(groupByDataTypeKeys); } } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to create tree because there is no current case", ex); //NON-NLS } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Exception while getting data from case.", ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to create tree because of an error querying the case database", ex); //NON-NLS } return true; } /** - * Creates nodes for the top level Key + * Creates a node for a given key for the top level nodes in the main tree + * view. * - * @param key + * @param key The key. * - * @return Node for the key, null if key is unknown. + * @return A node for the key. */ @Override protected Node createNodeForKey(Object key) { - if (key instanceof SleuthkitVisitableItem) { - return ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor()); - } else if (key instanceof AutopsyVisitableItem) { - return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor()); - } else { - logger.log(Level.SEVERE, "Unknown key type ", key.getClass().getName()); - return null; + Node node = null; + if (key != null) { + if (key instanceof SleuthkitVisitableItem) { + node = ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor()); + } else if (key instanceof AutopsyVisitableItem) { + node = ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor()); + } else { + logger.log(Level.SEVERE, "Unknown key type: ", key.getClass().getName()); + } } + return node; } /** - * Refresh the children + * Refreshes the top level nodes in the main tree view. */ public void refreshChildren() { refresh(true); } + } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index 033f14fc04..c8be68d164 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 61bc401ee5..ac3cd703c7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -197,7 +197,7 @@ public interface DisplayableItemNodeVisitor { T visit(OsAccounts.OsAccountListNode node); - T visit(PersonGroupingNode node); + T visit(PersonNode node); T visit(HostNode node); @@ -576,7 +576,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(PersonGroupingNode node) { + public T visit(PersonNode node) { return defaultVisit(node); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java rename to Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java index eccf0eb18a..c67758c056 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PersonGroupingNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2021 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. @@ -45,16 +45,21 @@ import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.TskCoreException; /** - * A node to be displayed in the UI tree for a person and persons grouped in - * this host. + * A main tree view node that represents a person in a case. Its child nodes, if + * any, represent hosts in the case. There must be at least one person in a case + * for the person nodes layer to appear. If the persons layer is present, any + * hosts that are not associated with a person are grouped under an "Unknown + * Persons" person node. */ @NbBundle.Messages(value = {"PersonNode_unknownPersonNode_title=Unknown Persons"}) -public class PersonGroupingNode extends DisplayableItemNode { +public class PersonNode extends DisplayableItemNode { private static final String ICON_PATH = "org/sleuthkit/autopsy/images/person.png"; - + /** - * Returns the id of an unknown persons node. This can be used with a node lookup. + * Returns the id of an unknown persons node. This can be used with a node + * lookup. + * * @return The id of an unknown persons node. */ public static String getUnknownPersonId() { @@ -68,14 +73,13 @@ public class PersonGroupingNode extends DisplayableItemNode { private static final Logger logger = Logger.getLogger(PersonChildren.class.getName()); - private static final Set CHILD_EVENTS = EnumSet.of(Case.Events.HOSTS_ADDED, - Case.Events.HOSTS_ADDED, - Case.Events.HOSTS_DELETED, - Case.Events.PERSONS_UPDATED, + private static final Set HOST_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.HOSTS_ADDED, + Case.Events.HOSTS_ADDED, + Case.Events.HOSTS_DELETED, Case.Events.HOSTS_ADDED_TO_PERSON, Case.Events.HOSTS_REMOVED_FROM_PERSON); - - private static final Set CHILD_EVENTS_STR = CHILD_EVENTS.stream() + + private static final Set HOST_EVENTS_OF_INTEREST_NAMES = HOST_EVENTS_OF_INTEREST.stream() .map(ev -> ev.name()) .collect(Collectors.toSet()); @@ -91,13 +95,18 @@ public class PersonGroupingNode extends DisplayableItemNode { } /** - * Listener for handling adding and removing host events. + * Listener for application events that are published when hosts are + * added to or deleted from a case, and for events published when the + * associations between persons and hosts change. If the user has + * selected the group by person/host option for the main tree view, + * these events mean that person nodes in the tree need to be refreshed + * to reflect the structural changes. */ private final PropertyChangeListener hostAddedDeletedPcl = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (eventType != null && CHILD_EVENTS_STR.contains(eventType)) { + if (eventType != null && HOST_EVENTS_OF_INTEREST_NAMES.contains(eventType)) { refresh(true); } } @@ -105,12 +114,12 @@ public class PersonGroupingNode extends DisplayableItemNode { @Override protected void addNotify() { - Case.addEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl); + Case.addEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl); } @Override protected void removeNotify() { - Case.removeEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl); + Case.removeEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl); } @Override @@ -141,7 +150,8 @@ public class PersonGroupingNode extends DisplayableItemNode { private final Long personId; /** - * Listener for handling person change events. + * Listener for application events that are published when the properties of + * persons in the case change. */ private final PropertyChangeListener personChangePcl = new PropertyChangeListener() { @Override @@ -163,6 +173,7 @@ public class PersonGroupingNode extends DisplayableItemNode { * Gets the display name for this person or "Unknown Persons". * * @param person The person. + * * @return The non-empty string for the display name. */ private static String getDisplayName(Person person) { @@ -176,17 +187,17 @@ public class PersonGroupingNode extends DisplayableItemNode { * * @param person The person record to be represented. */ - PersonGroupingNode(Person person) { + PersonNode(Person person) { this(person, getDisplayName(person)); } /** * Constructor. * - * @param person The person. + * @param person The person. * @param displayName The display name for the person. */ - private PersonGroupingNode(Person person, String displayName) { + private PersonNode(Person person, String displayName) { super(Children.create(new PersonChildren(person), false), person == null ? Lookups.fixed(displayName) : Lookups.fixed(person, displayName)); super.setName(displayName); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 07ea924d82..4e489312c8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -187,7 +187,7 @@ public class RootContentChildren extends Children.Keys { @Override public AbstractNode visit(PersonGrouping personGrouping) { - return new PersonGroupingNode(personGrouping.getPerson()); + return new PersonNode(personGrouping.getPerson()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index d2cd291de0..e7db0d4803 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -84,7 +84,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits; import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.ResultsNode; import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory; -import org.sleuthkit.autopsy.datamodel.PersonGroupingNode; +import org.sleuthkit.autopsy.datamodel.PersonNode; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.autopsy.datamodel.ViewsNode; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; @@ -234,7 +234,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat if (node == null) { return Collections.emptyList(); } else if (node.getLookup().lookup(Person.class) != null - || PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { + || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { Children children = node.getChildren(); Node[] childNodes = children == null ? null : children.getNodes(); if (childNodes != null) { @@ -1124,7 +1124,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat return null; } else if (node.getLookup().lookup(Host.class) != null || node.getLookup().lookup(Person.class) != null - || PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { + || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { Children children = node.getChildren(); Node[] childNodes = children == null ? null : children.getNodes(); if (childNodes != null) { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index 6fb134ceec..ee95a8ec80 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -49,7 +49,7 @@ import org.sleuthkit.autopsy.datamodel.ContentNodeSelectionInfo; import org.sleuthkit.autopsy.datamodel.DataSourcesByTypeNode; import org.sleuthkit.autopsy.datamodel.DataSourcesNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; -import org.sleuthkit.autopsy.datamodel.PersonGroupingNode; +import org.sleuthkit.autopsy.datamodel.PersonNode; import org.sleuthkit.autopsy.datamodel.RootContentChildren; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -330,7 +330,7 @@ public class ViewContextAction extends AbstractAction { } else if (node.getLookup().lookup(Host.class) != null || node.getLookup().lookup(Person.class) != null || DataSourcesByTypeNode.getNameIdentifier().equals(node.getLookup().lookup(String.class)) || - PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { + PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { Children children = node.getChildren(); Node[] childNodes = children == null ? null : children.getNodes(); if (childNodes == null) { From 358d9fef4a7bd3a1f6bf3ebc1c50618eb0207bfc Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 12 May 2021 10:13:38 -0400 Subject: [PATCH 12/78] 7553 data model event changes --- .../datamodel/DataSourceFilesNode.java | 182 ++++++++++++++++++ .../autopsy/datamodel/DataSources.java | 4 +- .../datamodel/DataSourcesByTypeNode.java | 155 --------------- .../autopsy/datamodel/DataSourcesNode.java | 166 +++++++--------- .../datamodel/DisplayableItemNodeVisitor.java | 8 +- .../datamodel/RootContentChildren.java | 4 +- .../directorytree/ViewContextAction.java | 10 +- 7 files changed, 269 insertions(+), 260 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/DataSourceFilesNode.java delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceFilesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceFilesNode.java new file mode 100644 index 0000000000..fd45f69bf0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceFilesNode.java @@ -0,0 +1,182 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2012-2021 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.datamodel; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.logging.Level; +import org.openide.nodes.Children; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * A structural node in the main tree view when the user has selected the group + * by persons/hosts option. Instances of this node appear as children of a node + * representing a data source association with a host, and as a parent of a data + * source node. For example: "Host X" -> "Data Source Y" -> "Data Source Files" + * -> "Data Source Y", where "Data Source Files" is an instance of this node. + */ +public class DataSourceFilesNode extends DisplayableItemNode { + + private static final String NAME = NbBundle.getMessage(DataSourceFilesNode.class, "DataSourcesNode.name"); + + /** + * @return The name used to identify the node of this type with a lookup. + */ + public static String getNameIdentifier() { + return NAME; + } + + private final String displayName; + + // NOTE: The images passed in via argument will be ignored. + @Deprecated + public DataSourceFilesNode(List images) { + this(0); + } + + public DataSourceFilesNode() { + this(0); + } + + public DataSourceFilesNode(long dsObjId) { + super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME)); + displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourceFilesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME; + init(); + } + + private void init() { + setName(NAME); + setDisplayName(displayName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + /* + * Custom Keys implementation that listens for new data sources being added. + */ + public static class DataSourcesNodeChildren extends AbstractContentChildren { + + private static final Logger logger = Logger.getLogger(DataSourcesNodeChildren.class.getName()); + private final long datasourceObjId; + + List currentKeys; + + public DataSourcesNodeChildren() { + this(0); + } + + public DataSourcesNodeChildren(long dsObjId) { + super("ds_" + Long.toString(dsObjId)); + this.currentKeys = new ArrayList<>(); + this.datasourceObjId = dsObjId; + } + + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + refresh(true); + } + } + }; + + @Override + protected void onAdd() { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); + } + + @Override + protected void onRemove() { + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); + currentKeys.clear(); + } + + @Override + protected List makeKeys() { + try { + if (datasourceObjId == 0) { + currentKeys = Case.getCurrentCaseThrows().getDataSources(); + } else { + Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId); + currentKeys = new ArrayList<>(Arrays.asList(content)); + } + + Collections.sort(currentKeys, new Comparator() { + @Override + public int compare(Content content1, Content content2) { + String content1Name = content1.getName().toLowerCase(); + String content2Name = content2.getName().toLowerCase(); + return content1Name.compareTo(content2Name); + } + + }); + + } catch (TskCoreException | NoCurrentCaseException | TskDataException ex) { + logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS + } + + return currentKeys; + } + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.displayName"), + NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.desc"), + NAME)); + return sheet; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSources.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSources.java index 52ca52e89f..4f23327401 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSources.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSources.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ package org.sleuthkit.autopsy.datamodel; /** - * Root node to store the data sources in a case + * An "Autopsy visitable item" that supplies a */ public class DataSources implements AutopsyVisitableItem { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java deleted file mode 100644 index 700875d161..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesByTypeNode.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2021 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.datamodel; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.stream.Collectors; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Root node for hosts displaying only data sources (no results, reports, etc.). - */ -@Messages({ - "DataSourcesHostsNode_name=Data Sources" -}) -public class DataSourcesByTypeNode extends DisplayableItemNode { - - /* - * Custom Keys implementation that listens for new data sources being added. - */ - public static class DataSourcesByTypeChildren extends ChildFactory.Detachable { - - private static final Set UPDATE_EVTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, - Case.Events.HOSTS_ADDED, - Case.Events.HOSTS_DELETED, - Case.Events.HOSTS_UPDATED); - - private static final Set UPDATE_EVT_STRS = UPDATE_EVTS.stream() - .map(evt -> evt.name()) - .collect(Collectors.toSet()); - - private static final Logger logger = Logger.getLogger(DataSourcesByTypeChildren.class.getName()); - - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (UPDATE_EVT_STRS.contains(eventType)) { - refresh(true); - } - } - }; - - @Override - protected void addNotify() { - Case.addEventTypeSubscriber(UPDATE_EVTS, pcl); - } - - @Override - protected void removeNotify() { - Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl); - } - - @Override - protected boolean createKeys(List toPopulate) { - try { - Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream() - .map(HostDataSources::new) - .sorted() - .forEach(toPopulate::add); - - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS - } - - return true; - } - - @Override - protected Node createNodeForKey(HostDataSources key) { - return new HostNode(key); - } - - } - - private static final String NAME = Bundle.DataSourcesHostsNode_name(); - - /** - * @return The name used to identify the node of this type with a lookup. - */ - public static String getNameIdentifier() { - return NAME; - } - - /** - * Main constructor. - */ - DataSourcesByTypeNode() { - super(Children.create(new DataSourcesByTypeChildren(), false), Lookups.singleton(NAME)); - setName(NAME); - setDisplayName(NAME); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor visitor) { - return visitor.visit(this); - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.desc"), - NAME)); - return sheet; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index c8be68d164..c5fae53c03 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2021 Basis Technology Corp. + * + * Copyright 2021 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. @@ -20,135 +20,117 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; +import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskDataException; /** - * Nodes for the images + * A top-level structural node (child of the invisible root node) in the main + * tree view when the user has selected the group by data type option. It + * appears as the parent node of the "directory tree" nodes that are the roots + * of the file trees for the individual data sources in a case. For example: + * "Data Sources" -> "Data Source X", "Data Source Y", where "Data Sources" is + * an instance of this node. The siblings of this node are the "Views, "Analysis + * Results," "Os Accounts," "Tags," and "Reports" nodes. */ +@Messages({ + "DataSourcesHostsNode_name=Data Sources" +}) public class DataSourcesNode extends DisplayableItemNode { - private static final String NAME = NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.name"); - - /** - * @return The name used to identify the node of this type with a lookup. - */ - public static String getNameIdentifier() { - return NAME; - } - - private final String displayName; - - // NOTE: The images passed in via argument will be ignored. - @Deprecated - public DataSourcesNode(List images) { - this(0); - } - - public DataSourcesNode() { - this(0); - } - - public DataSourcesNode(long dsObjId) { - super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME)); - displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME; - init(); - } - - private void init() { - setName(NAME); - setDisplayName(displayName); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS - } - - @Override - public String getItemType() { - return getClass().getName(); - } - /* * Custom Keys implementation that listens for new data sources being added. */ - public static class DataSourcesNodeChildren extends AbstractContentChildren { + public static class DataSourcesByTypeChildren extends ChildFactory.Detachable { - private static final Logger logger = Logger.getLogger(DataSourcesNodeChildren.class.getName()); - private final long datasourceObjId; - - List currentKeys; + private static final Set UPDATE_EVTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, + Case.Events.HOSTS_ADDED, + Case.Events.HOSTS_DELETED, + Case.Events.HOSTS_UPDATED); - public DataSourcesNodeChildren() { - this(0); - } + private static final Set UPDATE_EVT_STRS = UPDATE_EVTS.stream() + .map(evt -> evt.name()) + .collect(Collectors.toSet()); + + private static final Logger logger = Logger.getLogger(DataSourcesByTypeChildren.class.getName()); - public DataSourcesNodeChildren(long dsObjId) { - super("ds_" + Long.toString(dsObjId)); - this.currentKeys = new ArrayList<>(); - this.datasourceObjId = dsObjId; - } - private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); - if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + if (UPDATE_EVT_STRS.contains(eventType)) { refresh(true); } } }; @Override - protected void onAdd() { - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); + protected void addNotify() { + Case.addEventTypeSubscriber(UPDATE_EVTS, pcl); } @Override - protected void onRemove() { - Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); - currentKeys.clear(); + protected void removeNotify() { + Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl); } @Override - protected List makeKeys() { + protected boolean createKeys(List toPopulate) { try { - if (datasourceObjId == 0) { - currentKeys = Case.getCurrentCaseThrows().getDataSources(); - } - else { - Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId); - currentKeys = new ArrayList<>(Arrays.asList(content)); - } - - Collections.sort(currentKeys, new Comparator() { - @Override - public int compare(Content content1, Content content2) { - String content1Name = content1.getName().toLowerCase(); - String content2Name = content2.getName().toLowerCase(); - return content1Name.compareTo(content2Name); - } + Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream() + .map(HostDataSources::new) + .sorted() + .forEach(toPopulate::add); - }); - - } catch (TskCoreException | NoCurrentCaseException | TskDataException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS } - - return currentKeys; + + return true; } + + @Override + protected Node createNodeForKey(HostDataSources key) { + return new HostNode(key); + } + + } + + private static final String NAME = Bundle.DataSourcesHostsNode_name(); + + /** + * @return The name used to identify the node of this type with a lookup. + */ + public static String getNameIdentifier() { + return NAME; + } + + /** + * Main constructor. + */ + DataSourcesNode() { + super(Children.create(new DataSourcesByTypeChildren(), false), Lookups.singleton(NAME)); + setName(NAME); + setDisplayName(NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); + } + + @Override + public String getItemType() { + return getClass().getName(); } @Override @@ -176,4 +158,4 @@ public class DataSourcesNode extends DisplayableItemNode { NAME)); return sheet; } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index ac3cd703c7..9d5f6334f3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -42,7 +42,7 @@ public interface DisplayableItemNodeVisitor { /* * Data Sources Area */ - T visit(DataSourcesNode in); + T visit(DataSourceFilesNode in); T visit(LayoutFileNode lfn); @@ -201,7 +201,7 @@ public interface DisplayableItemNodeVisitor { T visit(HostNode node); - T visit(DataSourcesByTypeNode node); + T visit(DataSourcesNode node); /* * Unsupported node @@ -416,7 +416,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(DataSourcesNode in) { + public T visit(DataSourceFilesNode in) { return defaultVisit(in); } @@ -571,7 +571,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(DataSourcesByTypeNode node) { + public T visit(DataSourcesNode node) { return defaultVisit(node); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 4e489312c8..2db14ad2d4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -135,7 +135,7 @@ public class RootContentChildren extends Children.Keys { @Override public AbstractNode visit(DataSources i) { - return new DataSourcesNode(i.filteringDataSourceObjId()); + return new DataSourceFilesNode(i.filteringDataSourceObjId()); } @Override @@ -202,7 +202,7 @@ public class RootContentChildren extends Children.Keys { @Override public AbstractNode visit(DataSourcesByType dataSourceHosts) { - return new DataSourcesByTypeNode(); + return new DataSourcesNode(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index ee95a8ec80..c8f5848243 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -46,8 +46,8 @@ import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.ContentNodeSelectionInfo; -import org.sleuthkit.autopsy.datamodel.DataSourcesByTypeNode; import org.sleuthkit.autopsy.datamodel.DataSourcesNode; +import org.sleuthkit.autopsy.datamodel.DataSourceFilesNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.PersonNode; import org.sleuthkit.autopsy.datamodel.RootContentChildren; @@ -227,7 +227,7 @@ public class ViewContextAction extends AbstractAction { } // for this data source, get the "Data Sources" child node - Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourcesNode.getNameIdentifier()); + Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourceFilesNode.getNameIdentifier()); // check whether this is the data source we are looking for parentTreeViewNode = findParentNodeInTree(parentContent, datasourceGroupingNode); @@ -243,7 +243,7 @@ public class ViewContextAction extends AbstractAction { Node datasourceGroupingNode = rootChildren.findChild(dsname); if (!Objects.isNull(datasourceGroupingNode)) { Children dsChildren = datasourceGroupingNode.getChildren(); - parentTreeViewNode = dsChildren.findChild(DataSourcesNode.getNameIdentifier()); + parentTreeViewNode = dsChildren.findChild(DataSourceFilesNode.getNameIdentifier()); } } @@ -260,7 +260,7 @@ public class ViewContextAction extends AbstractAction { } else { // Classic view // Start the search at the DataSourcesNode Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren(); - Node rootDsNode = rootChildren == null ? null : rootChildren.findChild(DataSourcesByTypeNode.getNameIdentifier()); + Node rootDsNode = rootChildren == null ? null : rootChildren.findChild(DataSourcesNode.getNameIdentifier()); if (rootDsNode != null) { for (Node dataSourceLevelNode : getDataSourceLevelNodes(rootDsNode)) { DataSource dataSource = dataSourceLevelNode.getLookup().lookup(DataSource.class); @@ -329,7 +329,7 @@ public class ViewContextAction extends AbstractAction { return Collections.emptyList(); } else if (node.getLookup().lookup(Host.class) != null || node.getLookup().lookup(Person.class) != null || - DataSourcesByTypeNode.getNameIdentifier().equals(node.getLookup().lookup(String.class)) || + DataSourcesNode.getNameIdentifier().equals(node.getLookup().lookup(String.class)) || PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) { Children children = node.getChildren(); Node[] childNodes = children == null ? null : children.getNodes(); From 20d9c1d81308106ac59a46b8f57082567655f0a2 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 12 May 2021 16:57:11 -0400 Subject: [PATCH 13/78] first pass of changes --- .../DataContentViewerOtherCases.form | 1 + .../DataContentViewerOtherCases.java | 230 +++--------- .../OtherOccurrenceUtilities.java | 337 ++++++++++++++++++ .../contentviewer/OtherOccurrencesPanel.java | 135 +------ .../contentviewer/OtherOccurrencesWorker.java | 141 ++++++++ 5 files changed, 538 insertions(+), 306 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java create mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 60fb6c56df..ec0337e3b9 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -20,6 +20,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index cd901c0c1d..502c4b9481 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -19,33 +19,16 @@ package org.sleuthkit.autopsy.centralrepository.contentviewer; import java.awt.Component; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JPanel; import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; -import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardArtifactTag; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; -import org.sleuthkit.datamodel.TskException; /** * View correlation results from other cases @@ -57,13 +40,10 @@ import org.sleuthkit.datamodel.TskException; public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer { private static final long serialVersionUID = -1L; - private static final Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private final OtherOccurrencesPanel otherOccurrencesPanel = new OtherOccurrencesPanel(); - - /** - * Could be null. - */ - private AbstractFile file; //the file which the content viewer is being populated for + + private OtherOccurrencesWorker worker = null; /** * Creates new form DataContentViewerOtherCases @@ -104,146 +84,6 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon } - /** - * Get the associated BlackboardArtifact from a node, if it exists. - * - * @param node The node - * - * @return The associated BlackboardArtifact, or null - */ - private BlackboardArtifact - getBlackboardArtifactFromNode(Node node) { - BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class - ); - BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class - ); - - if (nodeBbArtifactTag != null) { - return nodeBbArtifactTag.getArtifact(); - } else if (nodeBbArtifact != null) { - return nodeBbArtifact; - } - - return null; - - } - - /** - * Get the associated AbstractFile from a node, if it exists. - * - * @param node The node - * - * @return The associated AbstractFile, or null - */ - private AbstractFile getAbstractFileFromNode(Node node) { - BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class - ); - ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class - ); - BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class - ); - AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class - ); - - if (nodeBbArtifactTag != null) { - Content content = nodeBbArtifactTag.getContent(); - if (content instanceof AbstractFile) { - return (AbstractFile) content; - } - } else if (nodeContentTag != null) { - Content content = nodeContentTag.getContent(); - if (content instanceof AbstractFile) { - return (AbstractFile) content; - } - } else if (nodeBbArtifact != null) { - Content content; - try { - content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID()); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS - return null; - } - - if (content instanceof AbstractFile) { - return (AbstractFile) content; - } - } else if (nodeAbstractFile != null) { - return nodeAbstractFile; - } - - return null; - } - - /** - * Determine what attributes can be used for correlation based on the node. - * If EamDB is not enabled, get the default Files correlation. - * - * @param node The node to correlate - * - * @return A list of attributes that can be used for correlation - */ - private Collection getCorrelationAttributesFromNode(Node node) { - Collection ret = new ArrayList<>(); - - // correlate on blackboard artifact attributes if they exist and supported - BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); - if (bbArtifact != null && CentralRepository.isEnabled()) { - ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact)); - } - - // we can correlate based on the MD5 if it is enabled - if (this.file != null && CentralRepository.isEnabled() && this.file.getSize() > 0) { - try { - - List artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes(); - String md5 = this.file.getMd5Hash(); - if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) { - for (CorrelationAttributeInstance.Type aType : artifactTypes) { - if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { - CorrelationCase corCase = CentralRepository.getInstance().getCase(Case.getCurrentCase()); - try { - ret.add(new CorrelationAttributeInstance( - aType, - md5, - corCase, - CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), - file.getParentPath() + file.getName(), - "", - file.getKnown(), - file.getId())); - } catch (CorrelationAttributeNormalizationException ex) { - LOGGER.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex); - } - break; - } - } - } - } catch (CentralRepoException | TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS - } - // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. - } else if (this.file != null && this.file.getSize() > 0) { - String md5 = this.file.getMd5Hash(); - if (md5 != null && !md5.isEmpty()) { - try { - final CorrelationAttributeInstance.Type fileAttributeType - = CorrelationAttributeInstance.getDefaultCorrelationTypes() - .stream() - .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) - .findAny() - .get(); - //The Central Repository is not enabled - ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, this.file.getId())); - } catch (CentralRepoException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS - } catch (CorrelationAttributeNormalizationException ex) { - LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS - } - } - } - return ret; - } - @Override public boolean isSupported(Node node) { @@ -251,41 +91,61 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon // - The central repo is enabled and the node has correlatable content // (either through the MD5 hash of the associated file or through a BlackboardArtifact) // - The central repo is disabled and the backing file has a valid MD5 hash - this.file = this.getAbstractFileFromNode(node); + AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); if (CentralRepository.isEnabled()) { - return !getCorrelationAttributesFromNode(node).isEmpty(); + return !OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file).isEmpty(); } else { - return this.file != null - && this.file.getSize() > 0 - && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); + return file != null + && file.getSize() > 0 + && ((file.getMd5Hash() != null) && (!file.getMd5Hash().isEmpty())); } } @Override public void setNode(Node node) { - otherOccurrencesPanel.reset(); // reset the table to empty. if (node == null) { return; } - //could be null - this.file = this.getAbstractFileFromNode(node); - String dataSourceName = ""; - String deviceId = ""; - try { - if (this.file != null) { - Content dataSource = this.file.getDataSource(); - dataSourceName = dataSource.getName(); - deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); - } - } catch (TskException | NoCurrentCaseException ex) { - // do nothing. - // @@@ Review this behavior + + if(worker != null) { + worker.cancel(true); } - otherOccurrencesPanel.populateTable(getCorrelationAttributesFromNode(node), dataSourceName, deviceId, file); + worker = new OtherOccurrencesWorker(node) { + @Override + public void done() { + try { + if(!isCancelled()) { + OtherOccurrencesData data = get(); + otherOccurrencesPanel.populateTable(data); + } + } catch (InterruptedException | ExecutionException ex) { + DataContentViewerOtherCases.logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel", ex); + } + } + }; + + worker.execute(); + + +// //could be null +// AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); +// String dataSourceName = ""; +// String deviceId = ""; +// try { +// if (file != null) { +// Content dataSource = file.getDataSource(); +// dataSourceName = dataSource.getName(); +// deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); +// } +// } catch (TskException | NoCurrentCaseException ex) { +// // do nothing. +// // @@@ Review this behavior +// } +// otherOccurrencesPanel.populateTable(OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file), dataSourceName, deviceId, file); } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java new file mode 100755 index 0000000000..61e5c9affe --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java @@ -0,0 +1,337 @@ +/* + * Central Repository + * + * Copyright 2021 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.centralrepository.contentviewer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.lang3.StringUtils; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * + */ +class OtherOccurrenceUtilities { + + private static final Logger logger = Logger.getLogger(OtherOccurrenceUtilities.class.getName()); + + private OtherOccurrenceUtilities() { + } + + /** + * Determine what attributes can be used for correlation based on the node. + * If EamDB is not enabled, get the default Files correlation. + * + * @param node The node to correlate + * + * @return A list of attributes that can be used for correlation + */ + static Collection getCorrelationAttributesFromNode(Node node, AbstractFile file) { + Collection ret = new ArrayList<>(); + + // correlate on blackboard artifact attributes if they exist and supported + BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); + if (bbArtifact != null && CentralRepository.isEnabled()) { + ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact)); + } + + // we can correlate based on the MD5 if it is enabled + if (file != null && CentralRepository.isEnabled() && file.getSize() > 0) { + try { + + List artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes(); + String md5 = file.getMd5Hash(); + if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) { + for (CorrelationAttributeInstance.Type aType : artifactTypes) { + if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { + CorrelationCase corCase = CentralRepository.getInstance().getCase(Case.getCurrentCase()); + try { + ret.add(new CorrelationAttributeInstance( + aType, + md5, + corCase, + CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), + file.getParentPath() + file.getName(), + "", + file.getKnown(), + file.getId())); + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex); + } + break; + } + } + } + } catch (CentralRepoException | TskCoreException ex) { + logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + } + // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. + } else if (file != null && file.getSize() > 0) { + String md5 = file.getMd5Hash(); + if (md5 != null && !md5.isEmpty()) { + try { + final CorrelationAttributeInstance.Type fileAttributeType + = CorrelationAttributeInstance.getDefaultCorrelationTypes() + .stream() + .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) + .findAny() + .get(); + //The Central Repository is not enabled + ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, file.getId())); + } catch (CentralRepoException ex) { + logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS + } + } + } + return ret; + } + + /** + * Get the associated BlackboardArtifact from a node, if it exists. + * + * @param node The node + * + * @return The associated BlackboardArtifact, or null + */ + static BlackboardArtifact getBlackboardArtifactFromNode(Node node) { + BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); + BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); + + if (nodeBbArtifactTag != null) { + return nodeBbArtifactTag.getArtifact(); + } else if (nodeBbArtifact != null) { + return nodeBbArtifact; + } + + return null; + + } + + /** + * Get the associated AbstractFile from a node, if it exists. + * + * @param node The node + * + * @return The associated AbstractFile, or null + */ + static AbstractFile getAbstractFileFromNode(Node node) { + BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); + ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class); + BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); + AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class); + + if (nodeBbArtifactTag != null) { + Content content = nodeBbArtifactTag.getContent(); + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeContentTag != null) { + Content content = nodeContentTag.getContent(); + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeBbArtifact != null) { + Content content; + try { + content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS + return null; + } + + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeAbstractFile != null) { + return nodeAbstractFile; + } + + return null; + } + + /** + * Query the central repo database (if enabled) and the case database to + * find all artifact instances correlated to the given central repository + * artifact. If the central repo is not enabled, this will only return files + * from the current case with matching MD5 hashes. + * + * @param corAttr CorrelationAttribute to query for + * + * @return A collection of correlated artifact instances + */ + static Map getCorrelatedInstances(AbstractFile file, String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) { + // @@@ Check exception + try { + final Case openCase = Case.getCurrentCaseThrows(); + String caseUUID = openCase.getName(); + HashMap nodeDataMap = new HashMap<>(); + + if (CentralRepository.isEnabled()) { + List instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); + + for (CorrelationAttributeInstance artifactInstance : instances) { + + // Only add the attribute if it isn't the object the user selected. + // We consider it to be a different object if at least one of the following is true: + // - the case UUID is different + // - the data source name is different + // - the data source device ID is different + // - the file path is different + if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) + || (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) + || (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) + || (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { + continue; + } + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); + UniquePathKey uniquePathKey = new UniquePathKey(newNode); + nodeDataMap.put(uniquePathKey, newNode); + } + if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) { + List caseDbFiles = getCaseDbMatches(corAttr, openCase, file); + + for (AbstractFile caseDbFile : caseDbFiles) { + addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile); + } + } + } + return nodeDataMap; + } catch (CentralRepoException ex) { + logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + } catch (TskCoreException ex) { + // do nothing. + // @@@ Review this behavior + logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS + } + + return new HashMap<>( + 0); + } + + /** + * Get all other abstract files in the current case with the same MD5 as the + * selected node. + * + * @param corAttr The CorrelationAttribute containing the MD5 to search for + * @param openCase The current case + * @param file The current file. + * + * @return List of matching AbstractFile objects + * + * @throws NoCurrentCaseException + * @throws TskCoreException + * @throws CentralRepoException + */ + static List getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException { + List caseDbArtifactInstances = new ArrayList<>(); + if (file != null) { + String md5 = corAttr.getCorrelationValue(); + SleuthkitCase tsk = openCase.getSleuthkitCase(); + List matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5})); + + for (AbstractFile fileMatch : matches) { + if (file.equals(fileMatch)) { + continue; // If this is the file the user clicked on + } + caseDbArtifactInstances.add(fileMatch); + } + } + return caseDbArtifactInstances; + + } + + + /** + * Adds the file to the nodeDataMap map if it does not already exist + * + * @param autopsyCase + * @param nodeDataMap + * @param newFile + * + * @throws TskCoreException + * @throws CentralRepoException + */ + static void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException { + + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); + + // If the caseDB object has a notable tag associated with it, update + // the known status to BAD + if (newNode.getKnown() != TskData.FileKnown.BAD) { + List fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile); + for (ContentTag tag : fileMatchTags) { + TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus(); + if (tagKnownStatus.equals(TskData.FileKnown.BAD)) { + newNode.updateKnown(TskData.FileKnown.BAD); + break; + } + } + } + + // Make a key to see if the file is already in the map + UniquePathKey uniquePathKey = new UniquePathKey(newNode); + + // If this node is already in the list, the only thing we need to do is + // update the known status to BAD if the caseDB version had known status BAD. + // Otherwise this is a new node so add the new node to the map. + if (nodeDataMap.containsKey(uniquePathKey)) { + if (newNode.getKnown() == TskData.FileKnown.BAD) { + OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey); + prevInstance.updateKnown(newNode.getKnown()); + } + } else { + nodeDataMap.put(uniquePathKey, newNode); + } + } + + /** + * Create a unique string to be used as a key for deduping data sources as + * best as possible + */ + static String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) { + return caseUUID + deviceId + dataSourceName; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java index d6588c2313..b2bbf66102 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java @@ -54,6 +54,7 @@ import org.joda.time.LocalDateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesWorker.OtherOccurrencesData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -408,14 +409,14 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { for (OtherOccurrenceNodeInstanceData nodeData : nodeDataMap.values()) { if (nodeData.isCentralRepoNode()) { try { - dataSources.add(makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); } catch (CentralRepoException ex) { logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); } } else { try { - dataSources.add(makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "No current case open for other occurrences", ex); @@ -458,37 +459,12 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { @NbBundle.Messages({ "OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources." }) - void populateTable(Collection correlationAttrs, String dataSourceName, String deviceId, AbstractFile abstractFile) { - this.file = abstractFile; - this.dataSourceName = dataSourceName; - this.deviceId = deviceId; - - // get the attributes we can correlate on - correlationAttributes.addAll(correlationAttrs); - Map caseNames = new HashMap<>(); - int totalCount = 0; - Set dataSources = new HashSet<>(); - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - for (OtherOccurrenceNodeInstanceData nodeData : getCorrelatedInstances(corAttr).values()) { - if (nodeData.isCentralRepoNode()) { - try { - dataSources.add(makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); - caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); - } - } else { - try { - dataSources.add(makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); - caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); - } catch (NoCurrentCaseException ex) { - logger.log(Level.WARNING, "No current case open for other occurrences", ex); - } - } - totalCount++; - } - } - for (CorrelationCase corCase : caseNames.values()) { + void populateTable(OtherOccurrencesData data) { + this.file = data.getFile(); + this.dataSourceName = data.getDataSourceName(); + this.deviceId = data.getDeviceId(); + + for (CorrelationCase corCase : data.getCaseMap().values()) { casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); } int caseCount = casesTableModel.getRowCount(); @@ -498,7 +474,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { casesTableModel.addCorrelationCase(NO_RESULTS_CASE); } setEarliestCaseDate(); - foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), totalCount, caseCount, dataSources.size())); + foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getInstanceDataCount(), caseCount, data.getDataSourceCount())); if (caseCount > 0) { casesTable.setRowSelectionInterval(0, 0); } @@ -543,10 +519,10 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { nodeDataMap.put(uniquePathKey, newNode); } if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) { - List caseDbFiles = getCaseDbMatches(corAttr, openCase, file); + List caseDbFiles = OtherOccurrenceUtilities.getCaseDbMatches(corAttr, openCase, file); for (AbstractFile caseDbFile : caseDbFiles) { - addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile); + OtherOccurrenceUtilities.addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile); } } } @@ -568,90 +544,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } /** - * Adds the file to the nodeDataMap map if it does not already exist - * - * @param autopsyCase - * @param nodeDataMap - * @param newFile - * - * @throws TskCoreException - * @throws CentralRepoException - */ - private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException { - - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); - - // If the caseDB object has a notable tag associated with it, update - // the known status to BAD - if (newNode.getKnown() != TskData.FileKnown.BAD) { - List fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile); - for (ContentTag tag : fileMatchTags) { - TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus(); - if (tagKnownStatus.equals(TskData.FileKnown.BAD)) { - newNode.updateKnown(TskData.FileKnown.BAD); - break; - } - } - } - - // Make a key to see if the file is already in the map - UniquePathKey uniquePathKey = new UniquePathKey(newNode); - - // If this node is already in the list, the only thing we need to do is - // update the known status to BAD if the caseDB version had known status BAD. - // Otherwise this is a new node so add the new node to the map. - if (nodeDataMap.containsKey(uniquePathKey)) { - if (newNode.getKnown() == TskData.FileKnown.BAD) { - OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey); - prevInstance.updateKnown(newNode.getKnown()); - } - } else { - nodeDataMap.put(uniquePathKey, newNode); - } - } - - /** - * Get all other abstract files in the current case with the same MD5 as the - * selected node. - * - * @param corAttr The CorrelationAttribute containing the MD5 to search for - * @param openCase The current case - * @param file The current file. - * - * @return List of matching AbstractFile objects - * - * @throws NoCurrentCaseException - * @throws TskCoreException - * @throws CentralRepoException - */ - private List getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException { - List caseDbArtifactInstances = new ArrayList<>(); - if (file != null) { - String md5 = corAttr.getCorrelationValue(); - SleuthkitCase tsk = openCase.getSleuthkitCase(); - List matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5})); - - for (AbstractFile fileMatch : matches) { - if (file.equals(fileMatch)) { - continue; // If this is the file the user clicked on - } - caseDbArtifactInstances.add(fileMatch); - } - } - return caseDbArtifactInstances; - - } - - /** - * Create a unique string to be used as a key for deduping data sources as - * best as possible - */ - private String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) { - return caseUUID + deviceId + dataSourceName; - } - - /** - * Updates diplayed information to be correct for the current case selection + * Updates displayed information to be correct for the current case selection */ private void updateOnCaseSelection() { int[] selectedCaseIndexes = casesTable.getSelectedRows(); @@ -699,7 +592,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } /** - * Updates diplayed information to be correct for the current data source + * Updates displayed information to be correct for the current data source * selection */ private void updateOnDataSourceSelection() { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java new file mode 100755 index 0000000000..704d8eac05 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java @@ -0,0 +1,141 @@ +/* + * Central Repository + * + * Copyright 2021 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.centralrepository.contentviewer; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesWorker.OtherOccurrencesData; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskException; + +/** + * + * + */ +class OtherOccurrencesWorker extends SwingWorker { + + private static final Logger logger = Logger.getLogger(OtherOccurrencesWorker.class.getName()); + + private final Node node; + + OtherOccurrencesWorker(Node node) { + this.node = node; + } + + @Override + protected OtherOccurrencesData doInBackground() throws Exception { + AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); + String deviceId = ""; + String dataSourceName = ""; + Map caseNames = new HashMap<>(); + try { + if (file != null) { + Content dataSource = file.getDataSource(); + deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); + dataSourceName = dataSource.getName(); + } + } catch (TskException | NoCurrentCaseException ex) { + // do nothing. + // @@@ Review this behavior + return null; + } + Collection correlationAttributes = OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file); + + int totalCount = 0; + Set dataSources = new HashSet<>(); + for (CorrelationAttributeInstance corAttr : correlationAttributes) { + for (OtherOccurrenceNodeInstanceData nodeData : OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr).values()) { + if (nodeData.isCentralRepoNode()) { + try { + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); + } + } else { + try { + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "No current case open for other occurrences", ex); + } + } + totalCount++; + } + } + + return new OtherOccurrencesData(file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size()); + } + + static class OtherOccurrencesData { + private final String deviceId; + private final AbstractFile file; + private final String dataSourceName; + private final Map caseMap; + private final int instanceDataCount; + private final int dataSourceCount; + + private OtherOccurrencesData(AbstractFile file, String dataSourceName, String deviceId, Map caseMap, int instanceCount, int dataSourceCount) { + this.file = file; + this.deviceId = deviceId; + this.dataSourceName = dataSourceName; + this.caseMap = caseMap; + this.instanceDataCount = instanceCount; + this.dataSourceCount = dataSourceCount; + } + + public String getDeviceId() { + return deviceId; + } + + public AbstractFile getFile() { + return file; + } + + public String getDataSourceName() { + return dataSourceName; + } + + public Map getCaseMap() { + return caseMap; + } + + public int getInstanceDataCount() { + return instanceDataCount; + } + + public int getDataSourceCount() { + return dataSourceCount; + } + + } +} From 14a21939a9a6418c587cc57dac341fa3d9e82d4b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 13 May 2021 07:55:59 -0400 Subject: [PATCH 14/78] working through hash set module --- .../hashdatabase/HashDbIngestModule.java | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 19b9ff28b7..e6f3826775 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -48,6 +48,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.Score.Significance; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -381,9 +383,8 @@ public class HashDbIngestModule implements FileIngestModule { totalCount.incrementAndGet(); file.setKnown(statusIfFound); - String hashSetName = db.getDisplayName(); String comment = generateComment(hashInfo); - if (!createArtifactIfNotExists(hashSetName, file, comment, db)) { + if (!createArtifactIfNotExists(file, comment, db)) { wasError = true; } } @@ -426,24 +427,23 @@ public class HashDbIngestModule implements FileIngestModule { /** * Creates a BlackboardArtifact if artifact does not already exist. * - * @param hashSetName The name of the hashset found. * @param file The file that had a hash hit. * @param comment The comment to associate with this artifact. * @param db the database in which this file was found. * * @return True if the operation occurred successfully and without error. */ - private boolean createArtifactIfNotExists(String hashSetName, AbstractFile file, String comment, HashDb db) { + private boolean createArtifactIfNotExists(AbstractFile file, String comment, HashDb db) { /* * We have a match. Now create an artifact if it is determined that one * hasn't been created yet. */ List attributesList = new ArrayList<>(); - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName)); + attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), db.getDisplayName())); try { Blackboard tskBlackboard = skCase.getBlackboard(); if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) { - postHashSetHitToBlackboard(file, file.getMd5Hash(), hashSetName, comment, db.getSendIngestMessages()); + postHashSetHitToBlackboard(file, file.getMd5Hash(), db, comment); } } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format( @@ -500,31 +500,57 @@ public class HashDbIngestModule implements FileIngestModule { totals.totalCalctime.addAndGet(delta); } + /** + * Converts HashDb.KnownFilesType to a Score to be used to create an analysis result. + * @param knownFilesType The HashDb KnownFilesType to convert. + * @return The Score to use when creating an AnalysisResult. + */ + private Score getScore(HashDb.KnownFilesType knownFilesType) { + if (knownFilesType == null) { + return Score.SCORE_UNKNOWN; + } + switch (knownFilesType) { + case KNOWN: + return new Score(Significance.NONE, Score.MethodCategory.AUTO); + case KNOWN_BAD: + return new Score(Significance.NOTABLE, Score.MethodCategory.AUTO); + default: + case NO_CHANGE: + return Score.SCORE_UNKNOWN; + } + } /** * Post a hash set hit to the blackboard. * * @param abstractFile The file to be processed. * @param md5Hash The MD5 hash value of the file. - * @param hashSetName The name of the hash set with which to associate - * the hit. + * @param db The database in which this file was found. * @param comment A comment to be attached to the artifact. - * @param showInboxMessage Show a message in the inbox? */ @Messages({ "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search." }) - private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) { + private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, HashDb db, String comment) { try { String moduleName = HashLookupModuleFactory.getModuleName(); - BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT); - Collection attributes = new ArrayList<>(); - //TODO Revisit usage of deprecated constructor as per TSK-583 - //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment)); + + List attributes = Arrays.asList( + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, db.getDisplayName()), + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash), + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment) + ); + - badFile.addAttributes(attributes); + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + + // BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection attributesList + BlackboardArtifact badFile = abstractFile.newAnalysisResult( + BlackboardArtifact.Type.TSK_HASHSET_HIT, getScore(db.getKnownFilesType()), + conclusion, configuration, justification, + attributes + ).getAnalysisResult(); try { /* @@ -538,7 +564,7 @@ public class HashDbIngestModule implements FileIngestModule { Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName()); } - if (showInboxMessage) { + if (db.getSendIngestMessages()) { StringBuilder detailsSb = new StringBuilder(); //details detailsSb.append(""); //NON-NLS @@ -563,7 +589,7 @@ public class HashDbIngestModule implements FileIngestModule { detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS detailsSb.append(""); //NON-NLS detailsSb.append("
") //NON-NLS .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.hashsetName")) .append("").append(hashSetName).append("").append(db.getDisplayName()).append("
"); //NON-NLS From c7ca74aa05095ac7e288a4cad216d9c20b405299 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 13 May 2021 08:38:51 -0400 Subject: [PATCH 15/78] initial changes for score and analysis results --- .../eventlisteners/IngestEventsListener.java | 9 +++++-- .../ingestmodule/CentralRepoIngestModule.java | 9 +++++-- .../dsp/AddLogicalImageTask.java | 16 ++++++++---- .../SevenZipExtractor.java | 11 ++++++-- .../filetypeid/FileTypeIdIngestModule.java | 9 +++++-- .../hashdatabase/HashDbIngestModule.java | 26 +++++++++++-------- .../FilesIdentifierIngestModule.java | 12 ++++++--- .../report/modules/stix/StixArtifactData.java | 10 +++++-- .../volatilityDSP/VolatilityProcessor.java | 9 +++++-- .../autopsy/keywordsearch/LuceneQuery.java | 10 ++++++- .../autopsy/keywordsearch/RegexQuery.java | 11 +++++++- 11 files changed, 98 insertions(+), 34 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 1de62fedd1..4b411b3a29 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -268,9 +268,14 @@ public class IngestEventsListener { Blackboard blackboard = tskCase.getBlackboard(); // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) { + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT), - Score.SCORE_UNKNOWN, null, null, null, attributesForNewArtifact) + BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributesForNewArtifact) .getAnalysisResult(); try { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index 6d2f0a7e40..ad150efc9e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -346,9 +346,14 @@ final class CentralRepoIngestModule implements FileIngestModule { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) { + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), - Score.SCORE_UNKNOWN, null, null, null, attributes) + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) .getAnalysisResult(); try { // index the artifact for keyword search diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java index 68bcaaef06..68d44b9d45 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java @@ -445,15 +445,21 @@ final class AddLogicalImageTask implements Runnable { private void addInterestingFileToArtifacts(long fileId, long dataSourceId, String ruleSetName, String ruleName, List artifacts) throws TskCoreException { BlackboardArtifact artifact; + + + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + try { artifact = this.blackboard.newAnalysisResult( - INTERESTING_FILE_TYPE, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, fileId, dataSourceId, - Score.SCORE_UNKNOWN, - null, - null, - null, + INTERESTING_HIT_SCORE, + conclusion, + configuration, + justification, Arrays.asList( new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName), new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index ec4d803fa1..ae235f1ac1 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -315,9 +315,16 @@ class SevenZipExtractor { details)); if (!blackboard.artifactExists(archiveFile, TSK_INTERESTING_FILE_HIT, attributes)) { - BlackboardArtifact artifact = rootArchive.getArchiveFile().newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + + BlackboardArtifact artifact = rootArchive.newAnalysisResult( + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) .getAnalysisResult(); + try { /* * post the artifact which will index the artifact for diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java index 644cc0aede..4ccde0afba 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java @@ -163,10 +163,15 @@ public class FileTypeIdIngestModule implements FileIngestModule { Blackboard tskBlackboard = currentCase.getSleuthkitCase().getBlackboard(); // Create artifact if it doesn't already exist. if (!tskBlackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + BlackboardArtifact artifact = file.newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) .getAnalysisResult(); - try { /* * post the artifact which will index the artifact for diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 55bad70850..56ff3bc89e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -49,10 +49,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.Score; -<<<<<<< HEAD import org.sleuthkit.datamodel.Score.Significance; -======= ->>>>>>> 7317-dataArtifacts import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -537,16 +534,23 @@ public class HashDbIngestModule implements FileIngestModule { try { String moduleName = HashLookupModuleFactory.getModuleName(); - Collection attributes = new ArrayList<>(); - //TODO Revisit usage of deprecated constructor as per TSK-583 - //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment)); + List attributes = Arrays.asList( + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, db.getDisplayName()), + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash), + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment) + ); + + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + // BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection attributesList BlackboardArtifact badFile = abstractFile.newAnalysisResult( - new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_HASHSET_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) - .getAnalysisResult(); + BlackboardArtifact.Type.TSK_HASHSET_HIT, getScore(db.getKnownFilesType()), + conclusion, configuration, justification, + attributes + ).getAnalysisResult(); + try { /* * post the artifact which will index the artifact for keyword diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java index 302e902095..29f8e99904 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java @@ -143,11 +143,15 @@ final class FilesIdentifierIngestModule implements FileIngestModule { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { - BlackboardArtifact artifact = file.newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) - .getAnalysisResult(); + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; - artifact.addAttributes(attributes); + BlackboardArtifact artifact = file.newAnalysisResult( + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) + .getAnalysisResult(); try { // Post thet artifact to the blackboard. diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java b/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java index 91822de3b8..4d26796800 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java @@ -88,9 +88,15 @@ class StixArtifactData { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + BlackboardArtifact bba = file.newAnalysisResult( - new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), - Score.SCORE_UNKNOWN, null, null, null, attributes).getAnalysisResult(); + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) + .getAnalysisResult(); try { /* diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java index 83cf2666b3..0220bc3bdf 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java @@ -385,9 +385,14 @@ class VolatilityProcessor { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(resolvedFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) { + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + BlackboardArtifact volArtifact = resolvedFile.newAnalysisResult( - new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT), - Score.SCORE_UNKNOWN, null, null, null, attributes) + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, + conclusion, configuration, justification, + attributes) .getAnalysisResult(); try { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 2d10d5cbbc..aaf7bef52d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -61,6 +61,7 @@ class LuceneQuery implements KeywordSearchQuery { static final int SNIPPET_LENGTH = 50; static final String HIGHLIGHT_FIELD = Server.Schema.TEXT.toString(); + private static final Score KEYWORD_SEARCH_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT); /** @@ -261,9 +262,16 @@ class LuceneQuery implements KeywordSearchQuery { hit.getArtifactID().ifPresent(artifactID -> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) ); + + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; try { - return content.newAnalysisResult(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_KEYWORD_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) + return content.newAnalysisResult( + BlackboardArtifact.Type.TSK_KEYWORD_HIT, KEYWORD_SEARCH_SCORE, + conclusion, configuration, justification, + attributes) .getAnalysisResult(); } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb artifact for keyword hit", e); //NON-NLS diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index ebaf078585..c1260b8a85 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -53,6 +53,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.Score.MethodCategory; +import org.sleuthkit.datamodel.Score.Significance; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -73,6 +75,7 @@ import org.sleuthkit.datamodel.TskData; final class RegexQuery implements KeywordSearchQuery { public static final Logger LOGGER = Logger.getLogger(RegexQuery.class.getName()); + private static final Score KEYWORD_SEARCH_SCORE = new Score(Significance.LIKELY_NOTABLE, MethodCategory.AUTO); /** * Lucene regular expressions do not support the following Java predefined @@ -613,8 +616,14 @@ final class RegexQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal())); } + String conclusion = TBD; + String configuration = TBD; + String justification = TBD; + try { - return content.newAnalysisResult(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_KEYWORD_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) + return content.newAnalysisResult( + BlackboardArtifact.Type.TSK_KEYWORD_HIT, KEYWORD_SEARCH_SCORE, + conclusion, configuration, justification, attributes) .getAnalysisResult(); } catch (TskCoreException e) { LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS From 7733aa79460a23013460d523263d3c558ed68d6e Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 13 May 2021 11:34:16 -0400 Subject: [PATCH 16/78] 7553 data model event changes --- .../sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java index 387b7d24e1..e1fb63a57b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java @@ -126,7 +126,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable Date: Thu, 13 May 2021 14:47:59 -0400 Subject: [PATCH 17/78] 7553 data model event changes --- Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java index c67758c056..3f01290353 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PersonNode.java @@ -131,7 +131,12 @@ public class PersonNode extends DisplayableItemNode { protected boolean createKeys(List toPopulate) { List hosts = Collections.emptyList(); try { - hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsForPerson(this.person); + if (person != null) { + hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsForPerson(person); + } else { + // This is the "Unknown Persons" node, get the hosts that are not associated with a person. + hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsWithoutPersons(); + } } catch (NoCurrentCaseException | TskCoreException ex) { String personName = person == null || person.getName() == null ? "" : person.getName(); logger.log(Level.WARNING, String.format("Unable to get data sources for host: %s", personName), ex); From 83e0bcb1e8a567bad687447ef9de47686816fcca Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 13 May 2021 15:20:49 -0400 Subject: [PATCH 18/78] analysis result scores --- .../eventlisteners/IngestEventsListener.java | 39 ++-- .../ingestmodule/CentralRepoIngestModule.java | 12 +- .../dsp/AddLogicalImageTask.java | 22 +- .../DataSourceIntegrityIngestModule.java | 7 +- .../SevenZipExtractor.java | 24 +- ...yptionDetectionDataSourceIngestModule.java | 13 +- .../EncryptionDetectionFileIngestModule.java | 15 +- .../FileExtMismatchIngestModule.java | 5 +- .../filetypeid/FileTypeIdIngestModule.java | 11 +- .../hashdatabase/HashDbIngestModule.java | 6 +- .../FilesIdentifierIngestModule.java | 11 +- .../leappanalyzers/LeappFileProcessor.java | 207 +++++++++--------- .../pictureanalyzer/impls/EXIFProcessor.java | 2 +- .../modules/yara/YaraIngestHelper.java | 6 +- .../report/modules/stix/StixArtifactData.java | 10 +- .../ObjectDetectectionFileIngestModule.java | 2 +- .../volatilityDSP/VolatilityProcessor.java | 18 +- .../autopsy/keywordsearch/LuceneQuery.java | 6 +- .../autopsy/keywordsearch/RegexQuery.java | 15 +- .../autopsy/recentactivity/Chromium.java | 19 +- .../autopsy/recentactivity/Extract.java | 2 - pythonExamples/dataSourceIngestModule.py | 4 +- pythonExamples/fileIngestModule.py | 4 +- .../ThunderbirdMboxFileIngestModule.java | 10 +- 24 files changed, 222 insertions(+), 248 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 4b411b3a29..21fb6ecd2f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -37,7 +37,6 @@ import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; @@ -63,12 +62,9 @@ import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; -import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; -import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; -import org.sleuthkit.datamodel.Account; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT; -import org.sleuthkit.datamodel.CommunicationsUtils; import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.Score.MethodCategory; +import org.sleuthkit.datamodel.Score.Significance; /** * Listen for ingest events and update entries in the Central Repository @@ -76,7 +72,7 @@ import org.sleuthkit.datamodel.Score; */ @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"}) public class IngestEventsListener { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Significance.LIKELY_NOTABLE, MethodCategory.AUTO); private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED); @@ -216,17 +212,17 @@ public class IngestEventsListener { @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", "IngestEventsListener.prevCaseComment.text=Previous Case: "}) static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List caseDisplayNames) { - - Collection attributesForNewArtifact = Arrays.asList(new BlackboardAttribute( - TSK_SET_NAME, MODULE_NAME, - Bundle.IngestEventsListener_prevTaggedSet_text()), + Collection attributesForNewArtifact = Arrays.asList( + new BlackboardAttribute( + TSK_SET_NAME, MODULE_NAME, + Bundle.IngestEventsListener_prevTaggedSet_text()), new BlackboardAttribute( TSK_COMMENT, MODULE_NAME, Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))), new BlackboardAttribute( TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, originalArtifact.getArtifactID())); - makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact); + makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevTaggedSet_text()); } /** @@ -251,31 +247,28 @@ public class IngestEventsListener { new BlackboardAttribute( TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, originalArtifact.getArtifactID())); - makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact); + makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevExists_text()); } - + + /** * Make an interesting item artifact to flag the passed in artifact. * * @param originalArtifact Artifact in current case we want to flag * @param attributesForNewArtifact Attributes to assign to the new * Interesting items artifact + * @param configuration The configuration to be specified for the new interesting artifact hit */ - private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection attributesForNewArtifact) { + private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection attributesForNewArtifact, String configuration) { try { SleuthkitCase tskCase = originalArtifact.getSleuthkitCase(); AbstractFile abstractFile = tskCase.getAbstractFileById(originalArtifact.getObjectID()); Blackboard blackboard = tskCase.getBlackboard(); // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - - BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, - attributesForNewArtifact) + BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult( + BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, LIKELY_NOTABLE_SCORE, + null, configuration, null, attributesForNewArtifact) .getAnalysisResult(); try { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index ad150efc9e..4a492d9af6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -67,7 +67,7 @@ import org.sleuthkit.datamodel.Score; final class CentralRepoIngestModule implements FileIngestModule { private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false; static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; @@ -334,7 +334,6 @@ final class CentralRepoIngestModule implements FileIngestModule { * @param caseDisplayNames Case names to be added to a TSK_COMMON attribute. */ private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List caseDisplayNames) { - Collection attributes = Arrays.asList( new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, @@ -346,14 +345,9 @@ final class CentralRepoIngestModule implements FileIngestModule { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, - attributes) + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, Bundle.CentralRepoIngestModule_prevTaggedSet_text(), null, attributes) .getAnalysisResult(); try { // index the artifact for keyword search diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java index 68d44b9d45..f8dc1730e2 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/AddLogicalImageTask.java @@ -29,7 +29,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -37,7 +36,6 @@ import java.util.Map; import java.util.logging.Level; import javax.annotation.concurrent.GuardedBy; import org.apache.commons.io.FileUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -102,8 +100,8 @@ final class AddLogicalImageTask implements Runnable { } } - private final static BlackboardArtifact.Type INTERESTING_FILE_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private final static Logger LOGGER = Logger.getLogger(AddLogicalImageTask.class.getName()); private final static String SEARCH_RESULTS_TXT = "SearchResults.txt"; //NON-NLS private final static String USERS_TXT = "_users.txt"; //NON-NLS @@ -445,21 +443,11 @@ final class AddLogicalImageTask implements Runnable { private void addInterestingFileToArtifacts(long fileId, long dataSourceId, String ruleSetName, String ruleName, List artifacts) throws TskCoreException { BlackboardArtifact artifact; - - - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - try { artifact = this.blackboard.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, - fileId, - dataSourceId, - INTERESTING_HIT_SCORE, - conclusion, - configuration, - justification, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, fileId, dataSourceId, + LIKELY_NOTABLE_SCORE, + null, ruleSetName, null, Arrays.asList( new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName), new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName) diff --git a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java index 82a917ce7e..d03a4db965 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/dataSourceIntegrity/DataSourceIntegrityIngestModule.java @@ -51,6 +51,7 @@ import org.sleuthkit.datamodel.TskDataException; */ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule { + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); private static final Logger logger = Logger.getLogger(DataSourceIntegrityIngestModule.class.getName()); private static final long DEFAULT_CHUNK_SIZE = 32 * 1024; private static final IngestServices services = IngestServices.getInstance(); @@ -294,10 +295,10 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule { if (!verified) { try { BlackboardArtifact verificationFailedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboard().newAnalysisResult( - new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_VERIFICATION_FAILED), + BlackboardArtifact.Type.TSK_VERIFICATION_FAILED, img.getId(), img.getId(), - Score.SCORE_UNKNOWN, - null, null, null, + NOTABLE_SCORE, + null, null, artifactComment, Arrays.asList(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, DataSourceIntegrityModuleFactory.getModuleName(), artifactComment))) .getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index ae235f1ac1..45be645ea2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -89,7 +89,9 @@ class SevenZipExtractor { private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName()); private static final String MODULE_NAME = EmbeddedFileExtractorModuleFactory.getModuleName(); - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); + //encryption type strings private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel"); @@ -302,11 +304,13 @@ class SevenZipExtractor { private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) { rootArchive.flagAsZipBomb(); logger.log(Level.INFO, details); + + String setName = "Possible Zip Bomb"; try { Collection attributes = Arrays.asList( new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, - "Possible Zip Bomb"), + setName), new BlackboardAttribute( TSK_DESCRIPTION, MODULE_NAME, Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())), @@ -315,13 +319,10 @@ class SevenZipExtractor { details)); if (!blackboard.artifactExists(archiveFile, TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - - BlackboardArtifact artifact = rootArchive.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, + + BlackboardArtifact artifact = archiveFile.newAnalysisResult( + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, setName, null, attributes) .getAnalysisResult(); @@ -862,8 +863,9 @@ class SevenZipExtractor { String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL; try { BlackboardArtifact artifact = archiveFile.newAnalysisResult( - new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED), Score.SCORE_UNKNOWN, - null, null, null, + new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED), + NOTABLE_SCORE, + null, null, encryptionType, Arrays.asList(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, encryptionType))) .getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java index ccc19843db..19c3001bb3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionDataSourceIngestModule.java @@ -47,6 +47,8 @@ import org.sleuthkit.datamodel.VolumeSystem; */ final class EncryptionDetectionDataSourceIngestModule implements DataSourceIngestModule { + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); private Blackboard blackboard; @@ -104,14 +106,16 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges return ProcessResult.OK; } if (BitlockerDetection.isBitlockerVolume(volume)) { - return flagVolume(volume, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_bitlocker()); + return flagVolume(volume, BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, NOTABLE_SCORE, + Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_bitlocker()); } if (context.dataSourceIngestIsCancelled()) { return ProcessResult.OK; } if (isVolumeEncrypted(volume)) { - return flagVolume(volume, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, String.format(Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_suspected(), calculatedEntropy)); + return flagVolume(volume, BlackboardArtifact.Type.TSK_ENCRYPTION_SUSPECTED, LIKELY_NOTABLE_SCORE, + String.format(Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_suspected(), calculatedEntropy)); } } // Update progress bar @@ -148,19 +152,20 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges * @param volume The volume to be processed. * @param artifactType The type of artifact to create. This is assumed to be * an analysis result type. + * @param score The score of the analysis result. * @param comment A comment to be attached to the artifact. * * @return 'OK' if the volume was processed successfully, or 'ERROR' if * there was a problem. */ - private IngestModule.ProcessResult flagVolume(Volume volume, BlackboardArtifact.ARTIFACT_TYPE artifactType, String comment) { + private IngestModule.ProcessResult flagVolume(Volume volume, BlackboardArtifact.Type artifactType, Score score, String comment) { if (context.dataSourceIngestIsCancelled()) { return ProcessResult.OK; } try { - BlackboardArtifact artifact = volume.newAnalysisResult(new BlackboardArtifact.Type(artifactType), Score.SCORE_UNKNOWN, null, null, null, + BlackboardArtifact artifact = volume.newAnalysisResult(artifactType, score, null, null, comment, Arrays.asList(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EncryptionDetectionModuleFactory.getModuleName(), comment))) .getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index 1609e4a5a6..96811833f5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -28,7 +28,6 @@ import com.healthmarketscience.jackcess.util.MemFileChannel; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.BufferUnderflowException; import java.util.Arrays; import java.util.logging.Level; import org.apache.tika.exception.EncryptedDocumentException; @@ -65,7 +64,9 @@ import org.xml.sax.SAXException; final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter { private static final int FILE_SIZE_MODULUS = 512; - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); + private static final String DATABASE_FILE_EXTENSION = "db"; private static final int MINIMUM_DATABASE_FILE_SIZE = 65536; //64 KB @@ -157,10 +158,11 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter */ String mimeType = fileTypeDetector.getMIMEType(file); if (mimeType.equals("application/octet-stream") && isFileEncryptionSuspected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, + return flagFile(file, BlackboardArtifact.Type.TSK_ENCRYPTION_SUSPECTED, LIKELY_NOTABLE_SCORE, String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy)); } else if (isFilePasswordProtected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); + return flagFile(file, BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, NOTABLE_SCORE, + Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); } } } catch (ReadContentInputStreamException | SAXException | TikaException | UnsupportedCodecException ex) { @@ -191,18 +193,19 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter * @param file The file to be processed. * @param artifactType The type of artifact to create. Assumed to be an * analysis result type. + * @param score The score of the analysis result. * @param comment A comment to be attached to the artifact. * * @return 'OK' if the file was processed successfully, or 'ERROR' if there * was a problem. */ - private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType, String comment) { + private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.Type artifactType, Score score, String comment) { try { if (context.fileIngestIsCancelled()) { return IngestModule.ProcessResult.OK; } - BlackboardArtifact artifact = file.newAnalysisResult(new BlackboardArtifact.Type(artifactType), Score.SCORE_UNKNOWN, null, null, null, + BlackboardArtifact artifact = file.newAnalysisResult(artifactType, score, null, null, comment, Arrays.asList(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EncryptionDetectionModuleFactory.getModuleName(), comment))) .getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java index b17523c69a..52a713eade 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java @@ -52,7 +52,8 @@ import org.sleuthkit.datamodel.TskException; "FileExtMismatchIngestModule.readError.message=Could not read settings." }) public class FileExtMismatchIngestModule implements FileIngestModule { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Logger logger = Logger.getLogger(FileExtMismatchIngestModule.class.getName()); private final IngestServices services = IngestServices.getInstance(); private final FileExtMismatchDetectorModuleSettings settings; @@ -143,7 +144,7 @@ public class FileExtMismatchIngestModule implements FileIngestModule { if (mismatchDetected) { // add artifact BlackboardArtifact bart = abstractFile.newAnalysisResult( - new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED), Score.SCORE_UNKNOWN, null, null, null, Collections.emptyList()) + BlackboardArtifact.Type.TSK_EXT_MISMATCH_DETECTED, LIKELY_NOTABLE_SCORE, null, null, null, Collections.emptyList()) .getAnalysisResult(); try { diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java index 4ccde0afba..9249a50750 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java @@ -49,7 +49,8 @@ import org.sleuthkit.datamodel.TskCoreException; */ @NbBundle.Messages({"CannotRunFileTypeDetection=Unable to run file type detection."}) public class FileTypeIdIngestModule implements FileIngestModule { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Logger logger = Logger.getLogger(FileTypeIdIngestModule.class.getName()); private static final HashMap totalsForIngestJobs = new HashMap<>(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); @@ -163,13 +164,9 @@ public class FileTypeIdIngestModule implements FileIngestModule { Blackboard tskBlackboard = currentCase.getSleuthkitCase().getBlackboard(); // Create artifact if it doesn't already exist. if (!tskBlackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - BlackboardArtifact artifact = file.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, fileType.getInterestingFilesSetName(), null, attributes) .getAnalysisResult(); try { diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 56ff3bc89e..bd3e55e8a2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -539,15 +539,11 @@ public class HashDbIngestModule implements FileIngestModule { new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash), new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment) ); - - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; // BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection attributesList BlackboardArtifact badFile = abstractFile.newAnalysisResult( BlackboardArtifact.Type.TSK_HASHSET_HIT, getScore(db.getKnownFilesType()), - conclusion, configuration, justification, + null, db.getDisplayName(), null, attributes ).getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java index 29f8e99904..be5aabb7fd 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java @@ -53,7 +53,8 @@ import org.sleuthkit.datamodel.TskData; */ @NbBundle.Messages({"FilesIdentifierIngestModule.getFilesError=Error getting interesting files sets from file."}) final class FilesIdentifierIngestModule implements FileIngestModule { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Object sharedResourcesLock = new Object(); private static final Logger logger = Logger.getLogger(FilesIdentifierIngestModule.class.getName()); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); @@ -143,13 +144,9 @@ final class FilesIdentifierIngestModule implements FileIngestModule { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - BlackboardArtifact artifact = file.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, filesSet.getName(), null, attributes) .getAnalysisResult(); try { diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java index 06ee9efe4c..f17f5469aa 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java @@ -108,10 +108,10 @@ public final class LeappFileProcessor { * Main constructor. * * @param attributeType The BlackboardAttribute type or null if not - * used. used. - * @param columnName The name of the column in the tsv file. - * @param required Whether or not this attribute is required to be - * present. + * used. used. + * @param columnName The name of the column in the tsv file. + * @param required Whether or not this attribute is required to be + * present. */ TsvColumn(BlackboardAttribute.Type attributeType, String columnName, boolean required) { this.attributeType = attributeType; @@ -275,7 +275,7 @@ public final class LeappFileProcessor { * Process the Leapp files that were found that match the xml mapping file * * @param LeappFilesToProcess List of files to process - * @param LeappImageFile Abstract file to create artifact for + * @param LeappImageFile Abstract file to create artifact for * * @throws FileNotFoundException * @throws IOException @@ -308,7 +308,7 @@ public final class LeappFileProcessor { * Process the Leapp files that were found that match the xml mapping file * * @param LeappFilesToProcess List of files to process - * @param dataSource The data source. + * @param dataSource The data source. * * @throws FileNotFoundException * @throws IOException @@ -318,7 +318,7 @@ public final class LeappFileProcessor { for (String LeappFileName : LeappFilesToProcess) { String fileName = FilenameUtils.getName(LeappFileName); - File LeappFile = new File(LeappFileName); + File LeappFile = new File(LeappFileName); if (tsvFileAttributes.containsKey(fileName)) { List attrList = tsvFileAttributes.get(fileName); BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName); @@ -345,7 +345,7 @@ public final class LeappFileProcessor { String trackpointSegmentName = null; GeoTrackPoints pointList = new GeoTrackPoints(); AbstractFile geoAbstractFile = null; - + if (LeappFile == null || !LeappFile.exists() || fileName == null) { logger.log(Level.WARNING, String.format("Leap file: %s is null or does not exist", LeappFile == null ? LeappFile.toString() : "")); return; @@ -405,11 +405,11 @@ public final class LeappFileProcessor { } } } - + try { if (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(), "norelationship").toLowerCase() == "trackpoint") { - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, "", geoAbstractFile)).addTrack(trackpointSegmentName, pointList, new ArrayList<>()); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, "", geoAbstractFile)).addTrack(trackpointSegmentName, pointList, new ArrayList<>()); + } } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS @@ -418,10 +418,9 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact.", - }) + "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact.",}) - private void createRoute (Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { + private void createRoute(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { Double startLatitude = Double.valueOf(0); Double startLongitude = Double.valueOf(0); @@ -435,7 +434,7 @@ public final class LeappFileProcessor { String sourceFile = null; AbstractFile absFile = null; String comment = ""; - + try { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { @@ -478,18 +477,16 @@ public final class LeappFileProcessor { GeoWaypoints waypointList = new GeoWaypoints(); waypointList.addPoint(new Waypoint(startLatitude, startLongitude, zeroValue, "")); waypointList.addPoint(new Waypoint(endLatitude, endLongitude, zeroValue, locationName)); - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addRoute(destinationName, dateTime, waypointList, new ArrayList<>()); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addRoute(destinationName, dateTime, waypointList, new ArrayList<>()); + } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_waypoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS } - - + } - + @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact.", - }) + "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact.",}) private AbstractFile createTrackpoint(Collection bbattributes, Content dataSource, String fileName, String trackpointSegmentName, GeoTrackPoints pointList) throws IngestModuleException { @@ -503,7 +500,7 @@ public final class LeappFileProcessor { String sourceFile = null; String comment = null; AbstractFile absFile = null; - + try { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { @@ -539,28 +536,26 @@ public final class LeappFileProcessor { absFile = (AbstractFile) dataSource; } if ((trackpointSegmentName == null) || (trackpointSegmentName == segmentName)) { - trackpointSegmentName = segmentName; - pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); + trackpointSegmentName = segmentName; + pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); } else { - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addTrack(segmentName, pointList, new ArrayList<>()); - trackpointSegmentName = segmentName; - pointList = new GeoTrackPoints(); - pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addTrack(segmentName, pointList, new ArrayList<>()); + trackpointSegmentName = segmentName; + pointList = new GeoTrackPoints(); + pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); + } } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_trackpoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS } - - return absFile; - + + return absFile; + } - @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.", - }) - + "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.",}) + private void createMessageRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String messageType = null; @@ -614,7 +609,7 @@ public final class LeappFileProcessor { sourceFile = bba.getValueString(); break; case "TSK_READ_STATUS": - if (bba.getValueInt() == 1 ) { + if (bba.getValueInt() == 1) { messageStatus = MessageReadStatus.READ; } else { messageStatus = MessageReadStatus.UNREAD; @@ -638,19 +633,19 @@ public final class LeappFileProcessor { AbstractFile absFile = findAbstractFile(dataSource, sourceFile); if (absFile == null) { absFile = (AbstractFile) dataSource; - } + } CommunicationArtifactsHelper accountArtifact; - Account.Type accountType = getAccountType(fileName); + Account.Type accountType = getAccountType(fileName); if (alternateId == null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact messageArtifact = accountArtifact.addMessage(messageType, communicationDirection, senderId, - receipentId, dateTime, messageStatus, subject, - messageText, threadId, otherAttributes); + receipentId, dateTime, messageStatus, subject, + messageText, threadId, otherAttributes); if (!fileAttachments.isEmpty()) { messageAttachments = new MessageAttachments(fileAttachments, new ArrayList<>()); accountArtifact.addAttachments(messageArtifact, messageAttachments); @@ -662,8 +657,7 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship.", - }) + "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship.",}) private void createContactRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String alternateId = null; @@ -715,14 +709,14 @@ public final class LeappFileProcessor { } Account.Type accountType = getAccountType(fileName); if (accountType != null) { - + CommunicationArtifactsHelper accountArtifact; if (alternateId == null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact messageArtifact = accountArtifact.addContact(contactName, phoneNumber, homePhoneNumber, mobilePhoneNumber, emailAddr, otherAttributes); } @@ -732,14 +726,13 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship.", - }) + "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship.",}) private void createCalllogRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String callerId = null; String alternateId = null; - List calleeId = Arrays.asList(); + List calleeId = Arrays.asList(); CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN; Long startDateTime = Long.valueOf(0); Long endDateTime = Long.valueOf(0); @@ -751,14 +744,14 @@ public final class LeappFileProcessor { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { case "TSK_TEXT_FILE": - sourceFile = bba.getValueString(); - break; + sourceFile = bba.getValueString(); + break; case "TSK_DATETIME_START": - startDateTime = bba.getValueLong(); - break; + startDateTime = bba.getValueLong(); + break; case "TSK_DATETIME_END": - startDateTime = bba.getValueLong(); - break; + startDateTime = bba.getValueLong(); + break; case "TSK_DIRECTION": if (bba.getValueString().toLowerCase().equals("outgoing")) { communicationDirection = CommunicationDirection.OUTGOING; @@ -773,8 +766,8 @@ public final class LeappFileProcessor { break; case "TSK_PHONE_NUMBER_TO": if (!bba.getValueString().isEmpty()) { - String [] calleeTempList = bba.getValueString().split(",", 0); - calleeId = Arrays.asList(calleeTempList); + String[] calleeTempList = bba.getValueString().split(",", 0); + calleeId = Arrays.asList(calleeTempList); } break; case "TSK_ID": @@ -786,12 +779,12 @@ public final class LeappFileProcessor { break; } } - + if (calleeId.isEmpty() && communicationDirection == CommunicationDirection.OUTGOING) { - String [] calleeTempList = callerId.split(",", 0); - calleeId = Arrays.asList(calleeTempList); - callerId = null; - } + String[] calleeTempList = callerId.split(",", 0); + calleeId = Arrays.asList(calleeTempList); + callerId = null; + } AbstractFile absFile = findAbstractFile(dataSource, sourceFile); if (absFile == null) { absFile = (AbstractFile) dataSource; @@ -800,10 +793,10 @@ public final class LeappFileProcessor { CommunicationArtifactsHelper accountArtifact; if (accountType != null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact callLogArtifact = accountArtifact.addCalllog(communicationDirection, callerId, calleeId, startDateTime, endDateTime, mediaType, otherAttributes); } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { @@ -811,7 +804,7 @@ public final class LeappFileProcessor { } } - + private Account.Type getAccountType(String AccountTypeName) { switch (AccountTypeName.toLowerCase()) { case "zapya.tsv": @@ -849,7 +842,7 @@ public final class LeappFileProcessor { case "whatsapp - contacts.tsv": return Account.Type.WHATSAPP; case "tangomessages messages.tsv": - return Account.Type.TANGO; + return Account.Type.TANGO; case "shareit file transfer.tsv": return Account.Type.SHAREIT; case "line - calllogs.tsv": @@ -880,20 +873,22 @@ public final class LeappFileProcessor { return Account.Type.PHONE; } } - + /** * Process the line read and create the necessary attributes for it. * - * @param lineValues List of column values. + * @param lineValues List of column values. * @param columnIndexes Mapping of column headers (trimmed; to lower case) - * to column index. All header columns and only all header columns should be - * present. - * @param attrList The list of attributes as specified for the schema of - * this file. - * @param fileName The name of the file being processed. - * @param lineNum The line number in the file. + * to column index. All header columns and only all + * header columns should be present. + * @param attrList The list of attributes as specified for the schema + * of this file. + * @param fileName The name of the file being processed. + * @param lineNum The line number in the file. + * * @return The collection of blackboard attributes for the artifact created - * from this line. + * from this line. + * * @throws IngestModuleException */ private Collection processReadLine(List lineValues, Map columnIndexes, @@ -949,9 +944,10 @@ public final class LeappFileProcessor { * Check type of attribute and possibly format string based on it. * * @param colAttr Column Attribute information - * @param value string to be formatted + * @param value string to be formatted + * * @return formatted string based on attribute type if no attribute type - * found then return original string + * found then return original string */ private String formatValueBasedOnAttrType(TsvColumn colAttr, String value) { if (colAttr.getAttributeType().getTypeName().equals("TSK_DOMAIN")) { @@ -971,9 +967,10 @@ public final class LeappFileProcessor { * value. * * @param attrType The attribute type. - * @param value The string value to be converted to the appropriate data - * type for the attribute type. + * @param value The string value to be converted to the appropriate data + * type for the attribute type. * @param fileName The file name that the value comes from. + * * @return The generated blackboard attribute. */ private BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String value, String fileName) { @@ -1022,7 +1019,9 @@ public final class LeappFileProcessor { * Handles converting a string value to a blackboard attribute. * * @param orig The original string value. + * * @return The generated blackboard attribute. + * * @throws ParseException * @throws NumberFormatException */ @@ -1033,13 +1032,15 @@ public final class LeappFileProcessor { * Runs parsing function on string value to convert to right data type and * generates a blackboard attribute for that converted data type. * - * @param value The string value. - * @param attrType The blackboard attribute type. - * @param fileName The name of the file from which the value comes. - * @param blankIsNull If string is blank return null attribute. - * @param zeroIsNull If string is some version of 0, return null attribute. + * @param value The string value. + * @param attrType The blackboard attribute type. + * @param fileName The name of the file from which the value comes. + * @param blankIsNull If string is blank return null attribute. + * @param zeroIsNull If string is some version of 0, return null + * attribute. * @param valueConverter The means of converting the string value to an - * appropriate blackboard attribute. + * appropriate blackboard attribute. + * * @return The generated blackboard attribute or null if not determined. */ private BlackboardAttribute parseAttrValue(String value, BlackboardAttribute.Type attrType, String fileName, boolean blankIsNull, boolean zeroIsNull, ParseExceptionFunction valueConverter) { @@ -1157,7 +1158,7 @@ public final class LeappFileProcessor { for (int k = 0; k < attributeNlist.getLength(); k++) { NamedNodeMap nnm = attributeNlist.item(k).getAttributes(); String attributeName = nnm.getNamedItem("attributename").getNodeValue(); - + if (!attributeName.toLowerCase().matches("null")) { String columnName = nnm.getNamedItem("columnName").getNodeValue(); String required = nnm.getNamedItem("required").getNodeValue(); @@ -1207,13 +1208,19 @@ public final class LeappFileProcessor { } /** - * Generic method for creating a blackboard artifact with attributes + * Generic method for creating a blackboard artifact with attributes + * + * NOTE: + * Handles Analysis results in a generic manner. If special handling for + * score, conclusion, configuration, justification is needed for an analysis + * result type, this will need to be updated. * - * @param artType The artifact type. - * @param dataSource is the Content object that needs to have the artifact - * added for it + * @param artType The artifact type. + * @param dataSource is the Content object that needs to have the artifact + * added for it * @param bbattributes is the collection of blackboard attributes that need - * to be added to the artifact after the artifact has been created + * to be added to the artifact after the artifact has + * been created * * @return The newly-created artifact, or null on error */ @@ -1238,7 +1245,7 @@ public final class LeappFileProcessor { * Method to post a list of BlackboardArtifacts to the blackboard. * * @param artifacts A list of artifacts. IF list is empty or null, the - * function will return. + * function will return. */ void postArtifacts(Collection artifacts) { if (artifacts == null || artifacts.isEmpty()) { @@ -1259,7 +1266,7 @@ public final class LeappFileProcessor { */ private void configExtractor() throws IOException { PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class, - xmlFile, true); + xmlFile, true); } private static final Set ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz")); @@ -1316,14 +1323,14 @@ public final class LeappFileProcessor { } } - + private AbstractFile findAbstractFile(Content dataSource, String fileNamePath) { if (fileNamePath == null) { return null; } - + List files; - + String fileName = FilenameUtils.getName(fileNamePath); String filePath = FilenameUtils.normalize(FilenameUtils.getPath(fileNamePath), true); @@ -1347,4 +1354,4 @@ public final class LeappFileProcessor { return null; } - } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/pictureanalyzer/impls/EXIFProcessor.java b/Core/src/org/sleuthkit/autopsy/modules/pictureanalyzer/impls/EXIFProcessor.java index 12872f69c1..1fe5964686 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/pictureanalyzer/impls/EXIFProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/pictureanalyzer/impls/EXIFProcessor.java @@ -153,7 +153,7 @@ public class EXIFProcessor implements PictureProcessor { final BlackboardArtifact exifArtifact = file.newDataArtifact(new BlackboardArtifact.Type(TSK_METADATA_EXIF), attributes); final BlackboardArtifact userSuspectedArtifact = file.newAnalysisResult( - new BlackboardArtifact.Type(TSK_USER_CONTENT_SUSPECTED), Score.SCORE_UNKNOWN, null, null, null, + BlackboardArtifact.Type.TSK_USER_CONTENT_SUSPECTED, Score.SCORE_UNKNOWN, null, null, null, Arrays.asList(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, Bundle.ExifProcessor_userContent_description()))) .getAnalysisResult(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java index cdc1e3f02e..6d75cac051 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java @@ -35,7 +35,6 @@ import org.sleuthkit.autopsy.yara.YaraJNIWrapper; import org.sleuthkit.autopsy.yara.YaraWrapperException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_YARA_HIT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_RULE; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -46,7 +45,8 @@ import org.sleuthkit.datamodel.TskCoreException; * Methods for scanning files for yara rule matches. */ final class YaraIngestHelper { - + + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); private static final String YARA_DIR = "yara"; private static final String YARA_C_EXE = "yarac64.exe"; private static final String MODULE_NAME = YaraIngestModuleFactory.getModuleName(); @@ -207,7 +207,7 @@ final class YaraIngestHelper { attributes.add(new BlackboardAttribute(TSK_SET_NAME, MODULE_NAME, ruleSetName)); attributes.add(new BlackboardAttribute(TSK_RULE, MODULE_NAME, rule)); - BlackboardArtifact artifact = abstractFile.newAnalysisResult(new BlackboardArtifact.Type(TSK_YARA_HIT), Score.SCORE_UNKNOWN, null, null, null, attributes) + BlackboardArtifact artifact = abstractFile.newAnalysisResult(BlackboardArtifact.Type.TSK_YARA_HIT, NOTABLE_SCORE, null, ruleSetName, rule, attributes) .getAnalysisResult(); artifacts.add(artifact); diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java b/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java index 4d26796800..108f1e5ac6 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/stix/StixArtifactData.java @@ -42,7 +42,7 @@ import org.sleuthkit.datamodel.TskCoreException; * */ class StixArtifactData { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); private static final String MODULE_NAME = "Stix"; private AbstractFile file; @@ -88,13 +88,9 @@ class StixArtifactData { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - BlackboardArtifact bba = file.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, setName, null, attributes) .getAnalysisResult(); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java index 1ac5a62d88..bff9d28be8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java @@ -171,7 +171,7 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter ); BlackboardArtifact artifact = file.newAnalysisResult( - new BlackboardArtifact.Type(TSK_OBJECT_DETECTED), Score.SCORE_UNKNOWN, null, null, null, attributes) + BlackboardArtifact.Type.TSK_OBJECT_DETECTED, Score.SCORE_UNKNOWN, null, null, null, attributes) .getAnalysisResult(); try { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java index 0220bc3bdf..6ed8437bdc 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java @@ -56,7 +56,8 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; * artifacts. */ class VolatilityProcessor { - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + private static final Logger logger = Logger.getLogger(VolatilityProcessor.class.getName()); private static final String VOLATILITY = "Volatility"; //NON-NLS private static final String VOLATILITY_EXECUTABLE = "volatility_2.6_win64_standalone.exe"; //NON-NLS @@ -377,21 +378,14 @@ class VolatilityProcessor { } try { - Collection attributes = singleton( - new BlackboardAttribute( - TSK_SET_NAME, VOLATILITY, - Bundle.VolatilityProcessor_artifactAttribute_interestingFileSet(pluginName)) - ); + String setName = Bundle.VolatilityProcessor_artifactAttribute_interestingFileSet(pluginName); + Collection attributes = singleton(new BlackboardAttribute(TSK_SET_NAME, VOLATILITY, setName)); // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(resolvedFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) { - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - BlackboardArtifact volArtifact = resolvedFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, INTERESTING_HIT_SCORE, - conclusion, configuration, justification, + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE, + null, setName, null, attributes) .getAnalysisResult(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index aaf7bef52d..f146b15f7f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -262,15 +262,11 @@ class LuceneQuery implements KeywordSearchQuery { hit.getArtifactID().ifPresent(artifactID -> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) ); - - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; try { return content.newAnalysisResult( BlackboardArtifact.Type.TSK_KEYWORD_HIT, KEYWORD_SEARCH_SCORE, - conclusion, configuration, justification, + null, listName, null, attributes) .getAnalysisResult(); } catch (TskCoreException e) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index c1260b8a85..234cc9ee69 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -48,13 +48,10 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; -import org.sleuthkit.datamodel.Score.MethodCategory; -import org.sleuthkit.datamodel.Score.Significance; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -75,8 +72,8 @@ import org.sleuthkit.datamodel.TskData; final class RegexQuery implements KeywordSearchQuery { public static final Logger LOGGER = Logger.getLogger(RegexQuery.class.getName()); - private static final Score KEYWORD_SEARCH_SCORE = new Score(Significance.LIKELY_NOTABLE, MethodCategory.AUTO); - + private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO); + /** * Lucene regular expressions do not support the following Java predefined * and POSIX character classes. There are other valid Java character classes @@ -616,14 +613,10 @@ final class RegexQuery implements KeywordSearchQuery { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal())); } - String conclusion = TBD; - String configuration = TBD; - String justification = TBD; - try { return content.newAnalysisResult( - BlackboardArtifact.Type.TSK_KEYWORD_HIT, KEYWORD_SEARCH_SCORE, - conclusion, configuration, justification, attributes) + BlackboardArtifact.Type.TSK_KEYWORD_HIT, LIKELY_NOTABLE_SCORE, + null, listName, null, attributes) .getAnalysisResult(); } catch (TskCoreException e) { LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java index c8a3bb64cd..c777e4cb81 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; +import java.util.Arrays; import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -59,6 +60,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; +import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper; @@ -67,7 +69,8 @@ import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper; * Chromium recent activity extraction */ class Chromium extends Extract { - + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); + private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS + "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS private static final String COOKIE_QUERY = "SELECT name, value, host_key, expires_utc,last_access_utc, creation_utc FROM cookies"; //NON-NLS @@ -823,11 +826,15 @@ class Chromium extends Extract { // get form address atifacts getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X); if (databaseEncrypted) { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, - RecentActivityExtracterModuleFactory.getModuleName(), - String.format("%s Autofill Database Encryption Detected", browser))); - bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, webDataFile, bbattributes)); + String comment = String.format("%s Autofill Database Encryption Detected", browser); + Collection bbattributes = Arrays.asList( + new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, + RecentActivityExtracterModuleFactory.getModuleName(), comment)); + + bbartifacts.add( + webDataFile.newAnalysisResult( + BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, NOTABLE_SCORE, + null, null, comment, bbattributes).getAnalysisResult()); } } catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) { logger.log(Level.SEVERE, String.format("Error adding artifacts to the case database " diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index c052ed2bbe..fc8cb67c65 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -34,7 +34,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Optional; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -51,7 +50,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; diff --git a/pythonExamples/dataSourceIngestModule.py b/pythonExamples/dataSourceIngestModule.py index 3645945aa7..bfe745b3a4 100644 --- a/pythonExamples/dataSourceIngestModule.py +++ b/pythonExamples/dataSourceIngestModule.py @@ -37,6 +37,7 @@ from java.lang import System from java.util.logging import Level from org.sleuthkit.datamodel import SleuthkitCase from org.sleuthkit.datamodel import AbstractFile +from org.sleuthkit.datamodel import Score from org.sleuthkit.datamodel import ReadContentInputStream from org.sleuthkit.datamodel import BlackboardArtifact from org.sleuthkit.datamodel import BlackboardAttribute @@ -85,6 +86,7 @@ class SampleJythonDataSourceIngestModuleFactory(IngestModuleFactoryAdapter): # Data Source-level ingest module. One gets created per data source. # TODO: Rename this to something more specific. Could just remove "Factory" from above name. class SampleJythonDataSourceIngestModule(DataSourceIngestModule): + LIKELY_NOTABLE_SCORE = Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO) _logger = Logger.getLogger(SampleJythonDataSourceIngestModuleFactory.moduleName) @@ -142,7 +144,7 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule): # artfiact. Refer to the developer docs for other examples. attrs = ArrayList() attrs.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, SampleJythonDataSourceIngestModuleFactory.moduleName, "Test file")) - art = file.newAnalysisResult(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, Score.SCORE_UNKNOWN, None, None, None, attrs) + art = file.newAnalysisResult(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, self.LIKELY_NOTABLE_SCORE, None, "Test file", None, attrs) try: # index the artifact for keyword search diff --git a/pythonExamples/fileIngestModule.py b/pythonExamples/fileIngestModule.py index a76d5c240e..e4aa12bab7 100644 --- a/pythonExamples/fileIngestModule.py +++ b/pythonExamples/fileIngestModule.py @@ -35,6 +35,7 @@ import jarray import inspect from java.lang import System from java.util.logging import Level +from org.sleuthkit.datamodel import Score from org.sleuthkit.datamodel import SleuthkitCase from org.sleuthkit.datamodel import AbstractFile from org.sleuthkit.datamodel import ReadContentInputStream @@ -88,6 +89,7 @@ class SampleJythonFileIngestModuleFactory(IngestModuleFactoryAdapter): # TODO: Rename this to something more specific. Could just remove "Factory" from above name. # Looks at the attributes of the passed in file. class SampleJythonFileIngestModule(FileIngestModule): + LIKELY_NOTABLE_SCORE = Score(Score.Significance.LIKELY_NOTABLE, Score.MethodCategory.AUTO) _logger = Logger.getLogger(SampleJythonFileIngestModuleFactory.moduleName) @@ -130,7 +132,7 @@ class SampleJythonFileIngestModule(FileIngestModule): attrs = ArrayList() attrs.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, SampleJythonFileIngestModuleFactory.moduleName, "Text Files")) - art = file.newAnalysisResult(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, Score.SCORE_UNKNOWN, None, None, None, attrs) + art = file.newAnalysisResult(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, self.LIKELY_NOTABLE_SCORE, None, "Text Files", None, attrs) try: diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index f18dfd3ae8..9bdec139b4 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -75,6 +75,7 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Fil * structure and metadata. */ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { + private static final Score NOTABLE_SCORE = new Score(Score.Significance.NOTABLE, Score.MethodCategory.AUTO); private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName()); private final IngestServices services = IngestServices.getInstance(); @@ -242,13 +243,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { // encrypted pst: Add encrypted file artifact try { + String encryptionFileLevel = NbBundle.getMessage(this.getClass(), + "ThunderbirdMboxFileIngestModule.encryptionFileLevel"); BlackboardArtifact artifact = abstractFile.newAnalysisResult( - new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED), - Score.SCORE_UNKNOWN, null, null, null, Arrays.asList( + BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, + NOTABLE_SCORE, null, null, encryptionFileLevel, Arrays.asList( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), - "ThunderbirdMboxFileIngestModule.encryptionFileLevel")) + encryptionFileLevel) )) .getAnalysisResult(); From 48de24eb91a213f31f3121e76321e9ff98209073 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 13 May 2021 17:24:13 -0400 Subject: [PATCH 19/78] Removed most of the other occurrences calls from the edt --- .../contentviewer/Bundle.properties-MERGED | 1 + .../DataContentViewerOtherCases.java | 38 +- .../OtherOccurrenceOneTypeWorker.java | 192 +++++++ .../OtherOccurrenceUtilities.java | 102 +++- ...r.java => OtherOccurrencesNodeWorker.java} | 58 +- .../contentviewer/OtherOccurrencesPanel.java | 512 +++++++----------- 6 files changed, 552 insertions(+), 351 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java rename Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/{OtherOccurrencesWorker.java => OtherOccurrencesNodeWorker.java} (72%) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED index 1151be4fb3..c76a70d950 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED @@ -52,3 +52,4 @@ OtherOccurrencesPanel.showCommonalityMenuItem.text=Show Frequency OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search. OtherOccurrencesPanel.table.noResultsFound=No results found. +OtherOccurrencesPanel_table_loadingResults=Loading results diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 502c4b9481..7bc8ddcb72 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.centralrepository.contentviewer; import java.awt.Component; +import java.awt.Cursor; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; @@ -42,8 +43,8 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon private static final long serialVersionUID = -1L; private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private final OtherOccurrencesPanel otherOccurrencesPanel = new OtherOccurrencesPanel(); - - private OtherOccurrencesWorker worker = null; + + private OtherOccurrencesNodeWorker worker = null; /** * Creates new form DataContentViewerOtherCases @@ -104,48 +105,33 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon @Override public void setNode(Node node) { otherOccurrencesPanel.reset(); // reset the table to empty. + otherOccurrencesPanel.showPanelLoadingMessage(); + if (node == null) { return; } - - if(worker != null) { + + if (worker != null) { worker.cancel(true); } - worker = new OtherOccurrencesWorker(node) { + worker = new OtherOccurrencesNodeWorker(node) { @Override public void done() { try { - if(!isCancelled()) { + if (!isCancelled()) { OtherOccurrencesData data = get(); otherOccurrencesPanel.populateTable(data); + otherOccurrencesPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } catch (InterruptedException | ExecutionException ex) { DataContentViewerOtherCases.logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel", ex); } } }; - + otherOccurrencesPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); worker.execute(); - - -// //could be null -// AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); -// String dataSourceName = ""; -// String deviceId = ""; -// try { -// if (file != null) { -// Content dataSource = file.getDataSource(); -// dataSourceName = dataSource.getName(); -// deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); -// } -// } catch (TskException | NoCurrentCaseException ex) { -// // do nothing. -// // @@@ Review this behavior -// } -// otherOccurrencesPanel.populateTable(OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file), dataSourceName, deviceId, file); - } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java new file mode 100755 index 0000000000..0e4a6e036d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java @@ -0,0 +1,192 @@ +/* + * Central Repository + * + * Copyright 2021 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.centralrepository.contentviewer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrenceOneTypeWorker.OneTypeData; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Swing worker for getting the Other Occurrence data for the Domain Discovery + * window. + * + * This logic differs a bit from the OtherOcurrencesNodeWorker. + */ +class OtherOccurrenceOneTypeWorker extends SwingWorker { + + private static final Logger logger = Logger.getLogger(OtherOccurrenceOneTypeWorker.class.getName()); + + private final CorrelationAttributeInstance.Type aType; + private final String value; + private final AbstractFile file; + private final String deviceId; + private final String dataSourceName; + + /** + * Construct the worker. + * + * @param aType + * @param value + * @param file Source file, this maybe null. + * @param deviceId DeviceID string, this maybe an empty string. + * @param dataSourceName DataSourceName, this maybe an empty string. + */ + OtherOccurrenceOneTypeWorker(CorrelationAttributeInstance.Type aType, String value, AbstractFile file, String deviceId, String dataSourceName) { + this.aType = aType; + this.value = value; + this.file = file; + this.deviceId = deviceId; + this.dataSourceName = dataSourceName; + } + + @Override + protected OneTypeData doInBackground() throws Exception { + Map caseNames = new HashMap<>(); + int totalCount = 0; + Set dataSources = new HashSet<>(); + Collection correlationAttributesToAdd = new ArrayList<>(); + String earliestDate = OtherOccurrenceUtilities.getEarliestCaseDate(); + OneTypeData results = null; + + if (CentralRepository.isEnabled()) { + List instances; + instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value); + HashMap nodeDataMap = new HashMap<>(); + String caseUUID = Case.getCurrentCase().getName(); + for (CorrelationAttributeInstance artifactInstance : instances) { + if (isCancelled()) { + break; + } + + // Only add the attribute if it isn't the object the user selected. + // We consider it to be a different object if at least one of the following is true: + // - the case UUID is different + // - the data source name is different + // - the data source device ID is different + // - the file path is different + if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) + || (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) + || (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) + || (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { + correlationAttributesToAdd.add(artifactInstance); + continue; + } + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, aType, value); + UniquePathKey uniquePathKey = new UniquePathKey(newNode); + nodeDataMap.put(uniquePathKey, newNode); + } + + for (OtherOccurrenceNodeInstanceData nodeData : nodeDataMap.values()) { + if (isCancelled()) { + break; + } + + if (nodeData.isCentralRepoNode()) { + try { + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); + } + } else { + try { + dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "No current case open for other occurrences", ex); + } + } + totalCount++; + } + } + + if (!isCancelled()) { + results = new OneTypeData(caseNames, totalCount, dataSources.size(), earliestDate, correlationAttributesToAdd); + } + + return results; + } + + /** + * Class to store the results of the worker thread. + */ + static final class OneTypeData { + + private final Map caseNames; + private final int totalCount; + private final int dataSourceCount; + private final Collection correlationAttributesToAdd; + private final String earliestCaseDate; + + /** + * Construct the results. + * + * @param caseNames Map of correlation cases. + * @param totalCount Total count of instances. + * @param dataSourceCount Data source count. + * @param earliestCaseDate Formatted string which contains the + * earliest case date. + * @param correlationAttributesToAdd The attributes to add to the main + * panel list. + */ + OneTypeData(Map caseNames, int totalCount, int dataSourceCount, String earliestCaseDate, Collection correlationAttributesToAdd) { + this.caseNames = caseNames; + this.totalCount = totalCount; + this.dataSourceCount = dataSourceCount; + this.correlationAttributesToAdd = correlationAttributesToAdd; + this.earliestCaseDate = earliestCaseDate; + } + + public Map getCaseNames() { + return caseNames; + } + + public int getTotalCount() { + return totalCount; + } + + public int getDataSourceCount() { + return dataSourceCount; + } + + public Collection getCorrelationAttributesToAdd() { + return correlationAttributesToAdd; + } + + public String getEarliestCaseDate() { + return earliestCaseDate; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java index 61e5c9affe..f43127f2e4 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java @@ -18,14 +18,25 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDateTime; import org.openide.nodes.Node; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; @@ -46,7 +57,8 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** - * + * Contains most of the methods for gathering data from the DB and CR for the + * OtherOccurrencesPanel. */ class OtherOccurrenceUtilities { @@ -187,14 +199,14 @@ class OtherOccurrenceUtilities { return null; } - + /** * Query the central repo database (if enabled) and the case database to * find all artifact instances correlated to the given central repository * artifact. If the central repo is not enabled, this will only return files * from the current case with matching MD5 hashes. * - * @param corAttr CorrelationAttribute to query for + * @param corAttr CorrelationAttribute to query for * * @return A collection of correlated artifact instances */ @@ -217,9 +229,9 @@ class OtherOccurrenceUtilities { // - the data source device ID is different // - the file path is different if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) - || (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) - || (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) - || (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { + && (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) + && (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) + && (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { continue; } OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); @@ -250,8 +262,8 @@ class OtherOccurrenceUtilities { return new HashMap<>( 0); } - - /** + + /** * Get all other abstract files in the current case with the same MD5 as the * selected node. * @@ -282,8 +294,7 @@ class OtherOccurrenceUtilities { return caseDbArtifactInstances; } - - + /** * Adds the file to the nodeDataMap map if it does not already exist * @@ -326,12 +337,79 @@ class OtherOccurrenceUtilities { nodeDataMap.put(uniquePathKey, newNode); } } - - /** + + /** * Create a unique string to be used as a key for deduping data sources as * best as possible */ static String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) { return caseUUID + deviceId + dataSourceName; } + + @NbBundle.Messages({"OtherOccurrencesPanel.earliestCaseNotAvailable= Not Enabled."}) + /** + * Gets the list of Eam Cases and determines the earliest case creation + * date. Sets the label to display the earliest date string to the user. + */ + static String getEarliestCaseDate() throws CentralRepoException { + String dateStringDisplay = Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable(); + + if (CentralRepository.isEnabled()) { + LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); + DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); + CentralRepository dbManager = CentralRepository.getInstance(); + List cases = dbManager.getCases(); + for (CorrelationCase aCase : cases) { + LocalDateTime caseDate; + try { + caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); + + if (caseDate.isBefore(earliestDate)) { + earliestDate = caseDate; + dateStringDisplay = aCase.getCreationDate(); + } + } catch (ParseException ex) { + throw new CentralRepoException("Failed to format case creation date " + aCase.getCreationDate(), ex); + } + } + } + + return dateStringDisplay; + } + + /** + * Create a cvs file of occurrences for the given parameters. + * + * @param destFile Output file for the csv data. + * @param abstractFile Source file. + * @param correlationAttList List of correclationAttributeInstances, should not be null. + * @param dataSourceName Name of the data source. + * @param deviceId Device id. + * + * @throws IOException + */ + static void writeOtherOccurrencesToFileAsCSV(File destFile, AbstractFile abstractFile, Collection correlationAttList, String dataSourceName, String deviceId) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) { + //write headers + StringBuilder headers = new StringBuilder("\""); + headers.append(Bundle.OtherOccurrencesPanel_csvHeader_case()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_dataSource()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_attribute()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_value()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_known()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_path()) + .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_comment()) + .append('"').append(System.getProperty("line.separator")); + writer.write(headers.toString()); + //write content + for (CorrelationAttributeInstance corAttr : correlationAttList) { + Map correlatedNodeDataMap = new HashMap<>(0); + // get correlation and reference set instances from DB + correlatedNodeDataMap.putAll(getCorrelatedInstances(abstractFile, deviceId, dataSourceName, corAttr)); + for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + writer.write(nodeData.toCsvString()); + } + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java similarity index 72% rename from Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java rename to Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java index 704d8eac05..7b317f86ee 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesWorker.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java @@ -28,7 +28,7 @@ import javax.swing.SwingWorker; import org.openide.nodes.Node; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesWorker.OtherOccurrencesData; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; @@ -38,16 +38,21 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskException; /** - * - * + * A SwingWorker that gathers data for the OtherOccurencesPanel which appears in + * the dataContentViewerOtherCases panel. */ -class OtherOccurrencesWorker extends SwingWorker { +class OtherOccurrencesNodeWorker extends SwingWorker { - private static final Logger logger = Logger.getLogger(OtherOccurrencesWorker.class.getName()); + private static final Logger logger = Logger.getLogger(OtherOccurrencesNodeWorker.class.getName()); private final Node node; - OtherOccurrencesWorker(Node node) { + /** + * Constructs a new instance for the given node. + * + * @param node + */ + OtherOccurrencesNodeWorker(Node node) { this.node = node; } @@ -57,13 +62,15 @@ class OtherOccurrencesWorker extends SwingWorker { String deviceId = ""; String dataSourceName = ""; Map caseNames = new HashMap<>(); + Case currentCase = Case.getCurrentCaseThrows(); + OtherOccurrencesData data = null; try { if (file != null) { Content dataSource = file.getDataSource(); - deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); + deviceId = currentCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); dataSourceName = dataSource.getName(); } - } catch (TskException | NoCurrentCaseException ex) { + } catch (TskException ex) { // do nothing. // @@@ Review this behavior return null; @@ -90,29 +97,46 @@ class OtherOccurrencesWorker extends SwingWorker { } } totalCount++; + + if (isCancelled()) { + break; + } } } - return new OtherOccurrencesData(file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size()); + if (!isCancelled()) { + data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrenceUtilities.getEarliestCaseDate()); + } + + return data; } + /** + * Object to store all of the data gathered in the OtherOccurrencesWorker + * doInBackground method. + */ static class OtherOccurrencesData { + private final String deviceId; private final AbstractFile file; private final String dataSourceName; private final Map caseMap; private final int instanceDataCount; private final int dataSourceCount; + private final String earliestCaseDate; + private final Collection correlationAttributes; - private OtherOccurrencesData(AbstractFile file, String dataSourceName, String deviceId, Map caseMap, int instanceCount, int dataSourceCount) { + private OtherOccurrencesData(Collection correlationAttributes, AbstractFile file, String dataSourceName, String deviceId, Map caseMap, int instanceCount, int dataSourceCount, String earliestCaseDate) { this.file = file; this.deviceId = deviceId; this.dataSourceName = dataSourceName; this.caseMap = caseMap; this.instanceDataCount = instanceCount; this.dataSourceCount = dataSourceCount; + this.earliestCaseDate = earliestCaseDate; + this.correlationAttributes = correlationAttributes; } - + public String getDeviceId() { return deviceId; } @@ -137,5 +161,17 @@ class OtherOccurrencesWorker extends SwingWorker { return dataSourceCount; } + /** + * Returns the earliest date in the case. + * + * @return Formatted date string, or message that one was not found. + */ + public String getEarliestCaseDate() { + return earliestCaseDate; + } + + public Collection getCorrelationAttributes() { + return correlationAttributes; + } } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java index b2bbf66102..a42ce7a276 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java @@ -18,26 +18,20 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; -import java.io.BufferedWriter; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JMenuItem; @@ -45,16 +39,14 @@ import javax.swing.JOptionPane; import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.ERROR_MESSAGE; import static javax.swing.JOptionPane.PLAIN_MESSAGE; +import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTimeZone; -import org.joda.time.LocalDateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesWorker.OtherOccurrencesData; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -62,21 +54,20 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNor import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; /** * Panel for displaying other occurrences results. */ @NbBundle.Messages({ "OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search.", - "OtherOccurrencesPanel.table.noResultsFound=No results found."}) + "OtherOccurrencesPanel.table.noResultsFound=No results found.", + "OtherOccurrencesPanel_table_loadingResults=Loading results" +}) public final class OtherOccurrencesPanel extends javax.swing.JPanel { private static final CorrelationCaseWrapper NO_ARTIFACTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noArtifacts()); private static final CorrelationCaseWrapper NO_RESULTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noResultsFound()); + private static final CorrelationCaseWrapper LOADING_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_loadingResults()); private static final String UUID_PLACEHOLDER_STRING = "NoCorrelationAttributeInstance"; private static final Logger logger = Logger.getLogger(OtherOccurrencesPanel.class.getName()); private static final long serialVersionUID = 1L; @@ -190,7 +181,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } else { StringBuilder msg = new StringBuilder(correlationAttributes.size()); int percentage; + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { + // Leaving these calls on the EDT but adding wait cursor CentralRepository dbManager = CentralRepository.getInstance(); for (CorrelationAttributeInstance eamArtifact : correlationAttributes) { try { @@ -202,11 +195,13 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { logger.log(Level.WARNING, String.format("Error getting commonality details for artifact with ID: %s.", eamArtifact.getID()), ex); } } + this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); JOptionPane.showConfirmDialog(showCommonalityMenuItem, msg.toString(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), DEFAULT_OPTION, PLAIN_MESSAGE); } catch (CentralRepoException ex) { + this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); logger.log(Level.SEVERE, "Error getting commonality details.", ex); JOptionPane.showConfirmDialog(showCommonalityMenuItem, Bundle.OtherOccurrencesPanel_correlatedArtifacts_failed(), @@ -272,7 +267,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS } - writeOtherOccurrencesToFileAsCSV(selectedFile); + CSVWorker worker = new CSVWorker(selectedFile, file, dataSourceName, deviceId, Collections.unmodifiableCollection(correlationAttributes)); + worker.execute(); } } } @@ -287,69 +283,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { "OtherOccurrencesPanel.csvHeader.path=Path", "OtherOccurrencesPanel.csvHeader.comment=Comment" }) - /** - * Write data for all cases in the content viewer to a CSV file - */ - private void writeOtherOccurrencesToFileAsCSV(File destFile) { - try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) { - //write headers - StringBuilder headers = new StringBuilder("\""); - headers.append(Bundle.OtherOccurrencesPanel_csvHeader_case()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_dataSource()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_attribute()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_value()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_known()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_path()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_comment()) - .append('"').append(System.getProperty("line.separator")); - writer.write(headers.toString()); - //write content - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); - // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { - writer.write(nodeData.toCsvString()); - } - } - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); - } - } - - @NbBundle.Messages({"OtherOccurrencesPanel.earliestCaseNotAvailable= Not Enabled."}) - /** - * Gets the list of Eam Cases and determines the earliest case creation - * date. Sets the label to display the earliest date string to the user. - */ - private void setEarliestCaseDate() { - String dateStringDisplay = Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable(); - - if (CentralRepository.isEnabled()) { - LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); - DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); - try { - CentralRepository dbManager = CentralRepository.getInstance(); - List cases = dbManager.getCases(); - for (CorrelationCase aCase : cases) { - LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); - - if (caseDate.isBefore(earliestDate)) { - earliestDate = caseDate; - dateStringDisplay = aCase.getCreationDate(); - } - - } - - } catch (CentralRepoException ex) { - logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS - } catch (ParseException ex) { - logger.log(Level.SEVERE, "Error parsing date of cases from database.", ex); // NON-NLS - } - - } - earliestCaseDate.setText(dateStringDisplay); - } /** * Reset the UI and clear cached data. @@ -372,89 +305,58 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * Populate the other occurrences table for one Correlation Attribute type * and value. * + * This method contains its own SwingWorker togather data. + * * @param aType The correlation attribute type to display other occurrences * for. * @param value The value being correlated on. */ - public void populateTableForOneType(CorrelationAttributeInstance.Type aType, String value) { - Map caseNames = new HashMap<>(); - int totalCount = 0; - Set dataSources = new HashSet<>(); - if (CentralRepository.isEnabled()) { + public void populateTableForOneType(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException { + casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); - try { - List instances; - instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value); - HashMap nodeDataMap = new HashMap<>(); - String caseUUID = Case.getCurrentCase().getName(); - for (CorrelationAttributeInstance artifactInstance : instances) { + OtherOccurrenceOneTypeWorker worker = new OtherOccurrenceOneTypeWorker(aType, value, file, deviceId, dataSourceName) { + @Override + public void done() { + try { + casesTableModel.clearTable(); - // Only add the attribute if it isn't the object the user selected. - // We consider it to be a different object if at least one of the following is true: - // - the case UUID is different - // - the data source name is different - // - the data source device ID is different - // - the file path is different - if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) - || (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) - || (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) - || (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { - correlationAttributes.add(artifactInstance); - continue; + OtherOccurrenceOneTypeWorker.OneTypeData data = get(); + for (CorrelationCase corCase : data.getCaseNames().values()) { + casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); } - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, aType, value); - UniquePathKey uniquePathKey = new UniquePathKey(newNode); - nodeDataMap.put(uniquePathKey, newNode); - } - for (OtherOccurrenceNodeInstanceData nodeData : nodeDataMap.values()) { - if (nodeData.isCentralRepoNode()) { - try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); - caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); - } - } else { - try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); - caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); - } catch (NoCurrentCaseException ex) { - logger.log(Level.WARNING, "No current case open for other occurrences", ex); - } + int caseCount = casesTableModel.getRowCount(); + if (correlationAttributes.isEmpty()) { + casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); + } else if (caseCount == 0) { + casesTableModel.addCorrelationCase(NO_RESULTS_CASE); } - totalCount++; + earliestCaseDate.setText(data.getEarliestCaseDate()); + foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getTotalCount(), caseCount, data.getDataSourceCount())); + if (caseCount > 0) { + casesTable.setRowSelectionInterval(0, 0); + } + + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Failed to "); } - } catch (CorrelationAttributeNormalizationException | CentralRepoException ex) { - logger.log(Level.WARNING, "Error retrieving other occurrences for " + aType.getDisplayName() + ": " + value, ex); } - } - for (CorrelationCase corCase : caseNames.values()) { - casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); - } - int caseCount = casesTableModel.getRowCount(); - if (correlationAttributes.isEmpty()) { - casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); - } else if (caseCount == 0) { - casesTableModel.addCorrelationCase(NO_RESULTS_CASE); - } - setEarliestCaseDate(); - foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), totalCount, caseCount, dataSources.size())); - if (caseCount > 0) { - casesTable.setRowSelectionInterval(0, 0); - } + }; + + worker.execute(); + } + + /** + * Makes a loading message appear in the case table. + */ + void showPanelLoadingMessage() { + casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); } /** * Load the correlatable data into the table model. If there is no data * available display the message on the status panel. * - * @param correlationAttrs The correlationAttributes to correlate on. - * @param dataSourceName The name of the dataSource to ignore results - * from. - * @param deviceId The deviceId of the device to ignore results - * from. - * @param abstractFile The abstract file to ignore files with the same - * location as. + * @param data A data wrapper object. */ @NbBundle.Messages({ "OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources." @@ -463,7 +365,11 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { this.file = data.getFile(); this.dataSourceName = data.getDataSourceName(); this.deviceId = data.getDeviceId(); - + + casesTableModel.clearTable(); + + correlationAttributes.addAll(data.getCorrelationAttributes()); + for (CorrelationCase corCase : data.getCaseMap().values()) { casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); } @@ -473,7 +379,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } else if (caseCount == 0) { casesTableModel.addCorrelationCase(NO_RESULTS_CASE); } - setEarliestCaseDate(); + earliestCaseDate.setText(data.getEarliestCaseDate()); foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getInstanceDataCount(), caseCount, data.getDataSourceCount())); if (caseCount > 0) { casesTable.setRowSelectionInterval(0, 0); @@ -481,113 +387,56 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } /** - * Query the central repo database (if enabled) and the case database to - * find all artifact instances correlated to the given central repository - * artifact. If the central repo is not enabled, this will only return files - * from the current case with matching MD5 hashes. - * - * @param corAttr CorrelationAttribute to query for - * - * @return A collection of correlated artifact instances - */ - private Map getCorrelatedInstances(CorrelationAttributeInstance corAttr) { - // @@@ Check exception - try { - final Case openCase = Case.getCurrentCaseThrows(); - String caseUUID = openCase.getName(); - HashMap nodeDataMap = new HashMap<>(); - - if (CentralRepository.isEnabled()) { - List instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); - - for (CorrelationAttributeInstance artifactInstance : instances) { - - // Only add the attribute if it isn't the object the user selected. - // We consider it to be a different object if at least one of the following is true: - // - the case UUID is different - // - the data source name is different - // - the data source device ID is different - // - the file path is different - if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) - || (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) - || (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) - || (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { - continue; - } - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); - UniquePathKey uniquePathKey = new UniquePathKey(newNode); - nodeDataMap.put(uniquePathKey, newNode); - } - if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) { - List caseDbFiles = OtherOccurrenceUtilities.getCaseDbMatches(corAttr, openCase, file); - - for (AbstractFile caseDbFile : caseDbFiles) { - OtherOccurrenceUtilities.addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile); - } - } - } - return nodeDataMap; - } catch (CentralRepoException ex) { - logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS - } catch (CorrelationAttributeNormalizationException ex) { - logger.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS - } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS - } catch (TskCoreException ex) { - // do nothing. - // @@@ Review this behavior - logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS - } - - return new HashMap<>( - 0); - } - - /** - * Updates displayed information to be correct for the current case selection + * Updates displayed information to be correct for the current case + * selection */ private void updateOnCaseSelection() { - int[] selectedCaseIndexes = casesTable.getSelectedRows(); - dataSourcesTableModel.clearTable(); - filesTableModel.clearTable(); - if (selectedCaseIndexes.length == 0) { - //special case when no cases are selected - occurrencePanel = new OccurrencePanel(); - occurrencePanel.getPreferredSize(); - detailsPanelScrollPane.setViewportView(occurrencePanel); - } else { - String currentCaseName; - try { - currentCaseName = Case.getCurrentCaseThrows().getName(); - } catch (NoCurrentCaseException ex) { - currentCaseName = null; - logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex); - } - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try{ + int[] selectedCaseIndexes = casesTable.getSelectedRows(); + dataSourcesTableModel.clearTable(); + filesTableModel.clearTable(); + if (selectedCaseIndexes.length == 0) { + //special case when no cases are selected + occurrencePanel = new OccurrencePanel(); + occurrencePanel.getPreferredSize(); + detailsPanelScrollPane.setViewportView(occurrencePanel); + } else { + String currentCaseName; + try { + currentCaseName = Case.getCurrentCaseThrows().getName(); + } catch (NoCurrentCaseException ex) { + currentCaseName = null; + logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex); + } + for (CorrelationAttributeInstance corAttr : correlationAttributes) { + Map correlatedNodeDataMap = new HashMap<>(0); - // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { - for (int selectedRow : selectedCaseIndexes) { - try { - if (nodeData.isCentralRepoNode()) { - if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null - && casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) { + // get correlation and reference set instances from DB + correlatedNodeDataMap.putAll(OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); + for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + for (int selectedRow : selectedCaseIndexes) { + try { + if (nodeData.isCentralRepoNode()) { + if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null + && casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) { + dataSourcesTableModel.addNodeData(nodeData); + } + } else if (currentCaseName != null && (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(currentCaseName))) { dataSourcesTableModel.addNodeData(nodeData); } - } else if (currentCaseName != null && (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(currentCaseName))) { - dataSourcesTableModel.addNodeData(nodeData); + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } } } + if (dataSourcesTable.getRowCount() > 0) { + dataSourcesTable.setRowSelectionInterval(0, 0); + } } - if (dataSourcesTable.getRowCount() > 0) { - dataSourcesTable.setRowSelectionInterval(0, 0); - } + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @@ -596,34 +445,39 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * selection */ private void updateOnDataSourceSelection() { - int[] selectedDataSources = dataSourcesTable.getSelectedRows(); - filesTableModel.clearTable(); - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try{ + int[] selectedDataSources = dataSourcesTable.getSelectedRows(); + filesTableModel.clearTable(); + for (CorrelationAttributeInstance corAttr : correlationAttributes) { + Map correlatedNodeDataMap = new HashMap<>(0); - // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { - for (int selectedDataSourceRow : selectedDataSources) { - try { - if (nodeData.isCentralRepoNode()) { - if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID()) - && dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { - filesTableModel.addNodeData(nodeData); - } - } else { - if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { - filesTableModel.addNodeData(nodeData); + // get correlation and reference set instances from DB + correlatedNodeDataMap.putAll(OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); + for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + for (int selectedDataSourceRow : selectedDataSources) { + try { + if (nodeData.isCentralRepoNode()) { + if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID()) + && dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { + filesTableModel.addNodeData(nodeData); + } + } else { + if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { + filesTableModel.addNodeData(nodeData); + } } + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } } } - } - if (filesTable.getRowCount() > 0) { - filesTable.setRowSelectionInterval(0, 0); + if (filesTable.getRowCount() > 0) { + filesTable.setRowSelectionInterval(0, 0); + } + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @@ -632,42 +486,47 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * currently selected File */ private void updateOnFileSelection() { - if (filesTable.getSelectedRowCount() == 1) { - //if there is one file selected update the deatils to show the data for that file - occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow()))); - } else if (dataSourcesTable.getSelectedRowCount() == 1) { - //if no files were selected and only one data source is selected update the information to reflect the data source - String caseName = dataSourcesTableModel.getCaseNameForRow(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow())); - String dsName = dataSourcesTableModel.getValueAt(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()), 0).toString(); - String caseCreatedDate = ""; - for (int row : casesTable.getSelectedRows()) { - if (casesTableModel.getValueAt(casesTable.convertRowIndexToModel(row), 0).toString().equals(caseName)) { - caseCreatedDate = getCaseCreatedDate(row); - break; + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try{ + if (filesTable.getSelectedRowCount() == 1) { + //if there is one file selected update the deatils to show the data for that file + occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow()))); + } else if (dataSourcesTable.getSelectedRowCount() == 1) { + //if no files were selected and only one data source is selected update the information to reflect the data source + String caseName = dataSourcesTableModel.getCaseNameForRow(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow())); + String dsName = dataSourcesTableModel.getValueAt(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()), 0).toString(); + String caseCreatedDate = ""; + for (int row : casesTable.getSelectedRows()) { + if (casesTableModel.getValueAt(casesTable.convertRowIndexToModel(row), 0).toString().equals(caseName)) { + caseCreatedDate = getCaseCreatedDate(row); + break; + } + } + occurrencePanel = new OccurrencePanel(caseName, caseCreatedDate, dsName); + } else if (casesTable.getSelectedRowCount() == 1) { + //if no files were selected and a number of data source other than 1 are selected + //update the information to reflect the case + String createdDate; + String caseName = ""; + if (casesTable.getRowCount() > 0) { + caseName = casesTableModel.getValueAt(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()), 0).toString(); + } + if (caseName.isEmpty()) { + occurrencePanel = new OccurrencePanel(); + } else { + createdDate = getCaseCreatedDate(casesTable.getSelectedRow()); + occurrencePanel = new OccurrencePanel(caseName, createdDate); } - } - occurrencePanel = new OccurrencePanel(caseName, caseCreatedDate, dsName); - } else if (casesTable.getSelectedRowCount() == 1) { - //if no files were selected and a number of data source other than 1 are selected - //update the information to reflect the case - String createdDate; - String caseName = ""; - if (casesTable.getRowCount() > 0) { - caseName = casesTableModel.getValueAt(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()), 0).toString(); - } - if (caseName.isEmpty()) { - occurrencePanel = new OccurrencePanel(); } else { - createdDate = getCaseCreatedDate(casesTable.getSelectedRow()); - occurrencePanel = new OccurrencePanel(caseName, createdDate); + //else display an empty details area + occurrencePanel = new OccurrencePanel(); } - } else { - //else display an empty details area - occurrencePanel = new OccurrencePanel(); + //calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible + occurrencePanel.getPreferredSize(); + detailsPanelScrollPane.setViewportView(occurrencePanel); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } - //calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible - occurrencePanel.getPreferredSize(); - detailsPanelScrollPane.setViewportView(occurrencePanel); } /** @@ -696,6 +555,55 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { return ""; } + /** + * SwingWorker for creating the CSV dump file. + */ + private class CSVWorker extends SwingWorker { + + private final Collection correlationAttList; + private final String dataSourceName; + private final String deviceId; + private final File destFile; + private final AbstractFile abstractFile; + + /** + * Construct a CSVWorker + * + * @param destFile Output file. + * @param sourceFile Input file. + * @param dataSourceName Name of current dataSource. + * @param deviceId Id of the selected device. + * @param correlationAttList + */ + CSVWorker(File destFile, AbstractFile sourceFile, String dataSourceName, String deviceId, Collection correlationAttList) { + this.destFile = destFile; + this.abstractFile = sourceFile; + this.dataSourceName = dataSourceName; + this.deviceId = deviceId; + this.correlationAttList = correlationAttList; + } + + @Override + protected Void doInBackground() throws Exception { + OtherOccurrenceUtilities.writeOtherOccurrencesToFileAsCSV(this.destFile, this.abstractFile, this.correlationAttList, this.dataSourceName, this.deviceId); + return null; + } + + @Override + public void done() { + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + JOptionPane.showMessageDialog(OtherOccurrencesPanel.this, + "Failed to create csv file for Other Occurrences at\n" + destFile.getAbsolutePath(), + "Error Creating CSV", + JOptionPane.ERROR_MESSAGE); + + logger.log(Level.SEVERE, "Error writing selected rows to from OtherOccurrencePanel to " + destFile.getAbsolutePath(), ex); + } + } + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always From 2ccf4555685c3d56b23532bf64729856265f8fe0 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 14 May 2021 13:52:55 -0400 Subject: [PATCH 20/78] Final format checking --- .../OtherOccurrenceOneTypeWorker.java | 2 +- .../contentviewer/OtherOccurrenceUtilities.java | 11 ++++++----- .../contentviewer/OtherOccurrencesPanel.java | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java index 5d917da242..e8e2da1939 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java @@ -100,7 +100,7 @@ class OtherOccurrenceOneTypeWorker extends SwingWorker { && (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) && (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) && (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { - + continue; } correlationAttributesToAdd.add(artifactInstance); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java index f43127f2e4..7ba5f5b301 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java @@ -380,11 +380,12 @@ class OtherOccurrenceUtilities { /** * Create a cvs file of occurrences for the given parameters. * - * @param destFile Output file for the csv data. - * @param abstractFile Source file. - * @param correlationAttList List of correclationAttributeInstances, should not be null. - * @param dataSourceName Name of the data source. - * @param deviceId Device id. + * @param destFile Output file for the csv data. + * @param abstractFile Source file. + * @param correlationAttList List of correclationAttributeInstances, should + * not be null. + * @param dataSourceName Name of the data source. + * @param deviceId Device id. * * @throws IOException */ diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java index 1a7024a86e..02bee6362f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java @@ -174,7 +174,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { */ private void showCommonalityDetails() { if (correlationAttributes.isEmpty()) { - JOptionPane.showConfirmDialog(showCommonalityMenuItem, + JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this, Bundle.OtherOccurrencesPanel_correlatedArtifacts_isEmpty(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), DEFAULT_OPTION, PLAIN_MESSAGE); @@ -196,14 +196,14 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } } this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - JOptionPane.showConfirmDialog(showCommonalityMenuItem, + JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this, msg.toString(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), DEFAULT_OPTION, PLAIN_MESSAGE); } catch (CentralRepoException ex) { this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); logger.log(Level.SEVERE, "Error getting commonality details.", ex); - JOptionPane.showConfirmDialog(showCommonalityMenuItem, + JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this, Bundle.OtherOccurrencesPanel_correlatedArtifacts_failed(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), DEFAULT_OPTION, ERROR_MESSAGE); @@ -245,7 +245,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } catch (CentralRepoException ex) { logger.log(Level.SEVERE, "Error loading case details", ex); } finally { - JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this, details, caseDisplayName, DEFAULT_OPTION, PLAIN_MESSAGE); @@ -392,7 +392,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { */ private void updateOnCaseSelection() { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try{ + try { int[] selectedCaseIndexes = casesTable.getSelectedRows(); dataSourcesTableModel.clearTable(); filesTableModel.clearTable(); @@ -446,7 +446,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { */ private void updateOnDataSourceSelection() { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try{ + try { int[] selectedDataSources = dataSourcesTable.getSelectedRows(); filesTableModel.clearTable(); for (CorrelationAttributeInstance corAttr : correlationAttributes) { @@ -477,7 +477,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { filesTable.setRowSelectionInterval(0, 0); } } finally { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @@ -487,7 +487,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { */ private void updateOnFileSelection() { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try{ + try { if (filesTable.getSelectedRowCount() == 1) { //if there is one file selected update the deatils to show the data for that file occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow()))); From a1c92306c98c757923646b8045b2ad2f83666715 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 17 May 2021 11:55:45 -0400 Subject: [PATCH 21/78] Added an application package to the cr package and renamed some classes --- .../application/Bundle.properties-MERGED | 8 ++ .../NodeData.java} | 34 ++++---- .../OtherOccurrences.java} | 80 ++++++++++++------- .../UniquePathKey.java | 9 ++- .../contentviewer/Bundle.properties-MERGED | 10 +-- .../DataContentViewerOtherCases.java | 5 +- .../contentviewer/OccurrencePanel.java | 28 +++---- .../OtherOccurrenceNodeData.java | 25 ------ .../OtherOccurrenceNodeMessageData.java | 34 -------- .../OtherOccurrenceOneTypeWorker.java | 15 ++-- ...OtherOccurrencesDataSourcesTableModel.java | 11 +-- .../OtherOccurrencesFilesTableModel.java | 18 +++-- .../OtherOccurrencesNodeWorker.java | 14 ++-- .../contentviewer/OtherOccurrencesPanel.java | 75 +++++++---------- 14 files changed, 160 insertions(+), 206 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/application/Bundle.properties-MERGED rename Core/src/org/sleuthkit/autopsy/centralrepository/{contentviewer/OtherOccurrenceNodeInstanceData.java => application/NodeData.java} (88%) rename Core/src/org/sleuthkit/autopsy/centralrepository/{contentviewer/OtherOccurrenceUtilities.java => application/OtherOccurrences.java} (83%) rename Core/src/org/sleuthkit/autopsy/centralrepository/{contentviewer => application}/UniquePathKey.java (92%) delete mode 100644 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java delete mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/application/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/application/Bundle.properties-MERGED new file mode 100755 index 0000000000..458d4b520b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/application/Bundle.properties-MERGED @@ -0,0 +1,8 @@ +OtherOccurrences.csvHeader.attribute=Matched Attribute +OtherOccurrences.csvHeader.case=Case +OtherOccurrences.csvHeader.comment=Comment +OtherOccurrences.csvHeader.dataSource=Data Source +OtherOccurrences.csvHeader.device=Device +OtherOccurrences.csvHeader.known=Known +OtherOccurrences.csvHeader.path=Path +OtherOccurrences.csvHeader.value=Attribute Value diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/application/NodeData.java similarity index 88% rename from Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java rename to Core/src/org/sleuthkit/autopsy/centralrepository/application/NodeData.java index 92ebc821f8..bae27ab680 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/application/NodeData.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.centralrepository.contentviewer; +package org.sleuthkit.autopsy.centralrepository.application; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.TskDataException; /** * Class for populating the Other Occurrences tab */ -class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { +public class NodeData { // For now hard code the string for the central repo files type, since // getting it dynamically can fail. @@ -56,7 +56,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * @param type The type of the instance * @param value The value of the instance */ - OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) { + public NodeData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) { caseName = instance.getCorrelationCase().getDisplayName(); deviceID = instance.getCorrelationDataSource().getDeviceID(); dataSourceName = instance.getCorrelationDataSource().getName(); @@ -77,7 +77,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @throws CentralRepoException */ - OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws CentralRepoException { + NodeData(AbstractFile newFile, Case autopsyCase) throws CentralRepoException { caseName = autopsyCase.getDisplayName(); try { DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); @@ -119,7 +119,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @param newComment The new comment */ - void updateComment(String newComment) { + public void updateComment(String newComment) { comment = newComment; } @@ -129,7 +129,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * @return true if this node was created from a central repo instance, false * otherwise */ - boolean isCentralRepoNode() { + public boolean isCentralRepoNode() { return (originalCorrelationInstance != null); } @@ -138,7 +138,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the case name */ - String getCaseName() { + public String getCaseName() { return caseName; } @@ -147,7 +147,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the device ID */ - String getDeviceID() { + public String getDeviceID() { return deviceID; } @@ -156,7 +156,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the data source name */ - String getDataSourceName() { + public String getDataSourceName() { return dataSourceName; } @@ -165,7 +165,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the file path */ - String getFilePath() { + public String getFilePath() { return filePath; } @@ -174,7 +174,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the type */ - String getType() { + public String getType() { return typeStr; } @@ -183,7 +183,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the value */ - String getValue() { + public String getValue() { return value; } @@ -192,7 +192,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the known status */ - TskData.FileKnown getKnown() { + public TskData.FileKnown getKnown() { return known; } @@ -201,7 +201,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the comment */ - String getComment() { + public String getComment() { return comment; } @@ -211,7 +211,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the original abstract file */ - AbstractFile getAbstractFile() throws CentralRepoException { + public AbstractFile getAbstractFile() throws CentralRepoException { if (originalAbstractFile == null) { throw new CentralRepoException("AbstractFile is null"); } @@ -226,7 +226,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @throws CentralRepoException */ - CorrelationAttributeInstance getCorrelationAttributeInstance() throws CentralRepoException { + public CorrelationAttributeInstance getCorrelationAttributeInstance() throws CentralRepoException { if (originalCorrelationInstance == null) { throw new CentralRepoException("CorrelationAttributeInstance is null"); } @@ -239,7 +239,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * * @return the CSV_ITEM_SEPARATOR string */ - static String getCsvItemSeparator() { + public static String getCsvItemSeparator() { return CSV_ITEM_SEPARATOR; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java b/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java similarity index 83% rename from Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java rename to Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java index 7ba5f5b301..934c8015a4 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.centralrepository.contentviewer; +package org.sleuthkit.autopsy.centralrepository.application; import java.io.BufferedWriter; import java.io.File; @@ -60,11 +60,13 @@ import org.sleuthkit.datamodel.TskData; * Contains most of the methods for gathering data from the DB and CR for the * OtherOccurrencesPanel. */ -class OtherOccurrenceUtilities { +public final class OtherOccurrences { - private static final Logger logger = Logger.getLogger(OtherOccurrenceUtilities.class.getName()); + private static final Logger logger = Logger.getLogger(OtherOccurrences.class.getName()); + + private static final String UUID_PLACEHOLDER_STRING = "NoCorrelationAttributeInstance"; - private OtherOccurrenceUtilities() { + private OtherOccurrences() { } /** @@ -75,7 +77,7 @@ class OtherOccurrenceUtilities { * * @return A list of attributes that can be used for correlation */ - static Collection getCorrelationAttributesFromNode(Node node, AbstractFile file) { + public static Collection getCorrelationAttributesFromNode(Node node, AbstractFile file) { Collection ret = new ArrayList<>(); // correlate on blackboard artifact attributes if they exist and supported @@ -144,7 +146,7 @@ class OtherOccurrenceUtilities { * * @return The associated BlackboardArtifact, or null */ - static BlackboardArtifact getBlackboardArtifactFromNode(Node node) { + public static BlackboardArtifact getBlackboardArtifactFromNode(Node node) { BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); @@ -165,7 +167,7 @@ class OtherOccurrenceUtilities { * * @return The associated AbstractFile, or null */ - static AbstractFile getAbstractFileFromNode(Node node) { + public static AbstractFile getAbstractFileFromNode(Node node) { BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class); BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); @@ -210,12 +212,12 @@ class OtherOccurrenceUtilities { * * @return A collection of correlated artifact instances */ - static Map getCorrelatedInstances(AbstractFile file, String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) { + public static Map getCorrelatedInstances(AbstractFile file, String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) { // @@@ Check exception try { final Case openCase = Case.getCurrentCaseThrows(); String caseUUID = openCase.getName(); - HashMap nodeDataMap = new HashMap<>(); + HashMap nodeDataMap = new HashMap<>(); if (CentralRepository.isEnabled()) { List instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); @@ -234,7 +236,7 @@ class OtherOccurrenceUtilities { && (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) { continue; } - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); + NodeData newNode = new NodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); UniquePathKey uniquePathKey = new UniquePathKey(newNode); nodeDataMap.put(uniquePathKey, newNode); } @@ -277,7 +279,7 @@ class OtherOccurrenceUtilities { * @throws TskCoreException * @throws CentralRepoException */ - static List getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException { + public static List getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException { List caseDbArtifactInstances = new ArrayList<>(); if (file != null) { String md5 = corAttr.getCorrelationValue(); @@ -305,9 +307,9 @@ class OtherOccurrenceUtilities { * @throws TskCoreException * @throws CentralRepoException */ - static void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException { + public static void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException { - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); + NodeData newNode = new NodeData(newFile, autopsyCase); // If the caseDB object has a notable tag associated with it, update // the known status to BAD @@ -330,7 +332,7 @@ class OtherOccurrenceUtilities { // Otherwise this is a new node so add the new node to the map. if (nodeDataMap.containsKey(uniquePathKey)) { if (newNode.getKnown() == TskData.FileKnown.BAD) { - OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey); + NodeData prevInstance = nodeDataMap.get(uniquePathKey); prevInstance.updateKnown(newNode.getKnown()); } } else { @@ -342,17 +344,16 @@ class OtherOccurrenceUtilities { * Create a unique string to be used as a key for deduping data sources as * best as possible */ - static String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) { + public static String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) { return caseUUID + deviceId + dataSourceName; } - - @NbBundle.Messages({"OtherOccurrencesPanel.earliestCaseNotAvailable= Not Enabled."}) + /** * Gets the list of Eam Cases and determines the earliest case creation * date. Sets the label to display the earliest date string to the user. */ - static String getEarliestCaseDate() throws CentralRepoException { - String dateStringDisplay = Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable(); + public static String getEarliestCaseDate() throws CentralRepoException { + String dateStringDisplay = ""; if (CentralRepository.isEnabled()) { LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); @@ -376,6 +377,17 @@ class OtherOccurrenceUtilities { return dateStringDisplay; } + + @NbBundle.Messages({ + "OtherOccurrences.csvHeader.case=Case", + "OtherOccurrences.csvHeader.device=Device", + "OtherOccurrences.csvHeader.dataSource=Data Source", + "OtherOccurrences.csvHeader.attribute=Matched Attribute", + "OtherOccurrences.csvHeader.value=Attribute Value", + "OtherOccurrences.csvHeader.known=Known", + "OtherOccurrences.csvHeader.path=Path", + "OtherOccurrences.csvHeader.comment=Comment" + }) /** * Create a cvs file of occurrences for the given parameters. @@ -389,28 +401,38 @@ class OtherOccurrenceUtilities { * * @throws IOException */ - static void writeOtherOccurrencesToFileAsCSV(File destFile, AbstractFile abstractFile, Collection correlationAttList, String dataSourceName, String deviceId) throws IOException { + public static void writeOtherOccurrencesToFileAsCSV(File destFile, AbstractFile abstractFile, Collection correlationAttList, String dataSourceName, String deviceId) throws IOException { try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) { //write headers StringBuilder headers = new StringBuilder("\""); - headers.append(Bundle.OtherOccurrencesPanel_csvHeader_case()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_dataSource()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_attribute()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_value()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_known()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_path()) - .append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_comment()) + headers.append(Bundle.OtherOccurrences_csvHeader_case()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_dataSource()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_attribute()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_value()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_known()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_path()) + .append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_comment()) .append('"').append(System.getProperty("line.separator")); writer.write(headers.toString()); //write content for (CorrelationAttributeInstance corAttr : correlationAttList) { - Map correlatedNodeDataMap = new HashMap<>(0); + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB correlatedNodeDataMap.putAll(getCorrelatedInstances(abstractFile, deviceId, dataSourceName, corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + for (NodeData nodeData : correlatedNodeDataMap.values()) { writer.write(nodeData.toCsvString()); } } } } + + /** + * Get a placeholder string to use in place of case uuid when it isn't + * available + * + * @return UUID_PLACEHOLDER_STRING + */ + public static String getPlaceholderUUID() { + return UUID_PLACEHOLDER_STRING; + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/UniquePathKey.java b/Core/src/org/sleuthkit/autopsy/centralrepository/application/UniquePathKey.java similarity index 92% rename from Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/UniquePathKey.java rename to Core/src/org/sleuthkit/autopsy/centralrepository/application/UniquePathKey.java index fd11c4e70b..6de3ff2799 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/UniquePathKey.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/application/UniquePathKey.java @@ -16,12 +16,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.centralrepository.contentviewer; +package org.sleuthkit.autopsy.centralrepository.application; import java.util.Objects; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesPanel; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -29,7 +30,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Used as a key to ensure we eliminate duplicates from the result set by not * overwriting CR correlation instances. */ -final class UniquePathKey { +public final class UniquePathKey { private static final Logger logger = Logger.getLogger(UniquePathKey.class.getName()); private final String dataSourceID; @@ -37,7 +38,7 @@ final class UniquePathKey { private final String type; private final String caseUUID; - UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) { + public UniquePathKey(NodeData nodeData) { super(); dataSourceID = nodeData.getDeviceID(); if (nodeData.getFilePath() != null) { @@ -56,7 +57,7 @@ final class UniquePathKey { //place holder value will be used since correlation attribute was unavailble } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Unable to get current case", ex); - tempCaseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); + tempCaseUUID = OtherOccurrences.getPlaceholderUUID(); } } caseUUID = tempCaseUUID; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED index c76a70d950..e4507c7d3b 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties-MERGED @@ -31,18 +31,9 @@ OtherOccurrencesPanel.correlatedArtifacts.byType={0}% of data sources have {2} ( OtherOccurrencesPanel.correlatedArtifacts.failed=Failed to get frequency details. OtherOccurrencesPanel.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate. OtherOccurrencesPanel.correlatedArtifacts.title=Attribute Frequency -OtherOccurrencesPanel.csvHeader.attribute=Matched Attribute -OtherOccurrencesPanel.csvHeader.case=Case -OtherOccurrencesPanel.csvHeader.comment=Comment -OtherOccurrencesPanel.csvHeader.dataSource=Data Source -OtherOccurrencesPanel.csvHeader.device=Device -OtherOccurrencesPanel.csvHeader.known=Known -OtherOccurrencesPanel.csvHeader.path=Path -OtherOccurrencesPanel.csvHeader.value=Attribute Value OtherOccurrencesPanel.earliestCaseLabel.toolTipText= OtherOccurrencesPanel.earliestCaseLabel.text=Central Repository Starting Date: OtherOccurrencesPanel.earliestCaseDate.text=Earliest Case Date -OtherOccurrencesPanel.earliestCaseNotAvailable=\ Not Enabled. OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources. OtherOccurrencesPanel.foundInLabel.text= OtherOccurrencesPanel.filesTable.toolTipText=Click column name to sort. Right-click on the table for more options. @@ -52,4 +43,5 @@ OtherOccurrencesPanel.showCommonalityMenuItem.text=Show Frequency OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search. OtherOccurrencesPanel.table.noResultsFound=No results found. +OtherOccurrencesPanel_earliestCaseNotAvailable=Not Availble. OtherOccurrencesPanel_table_loadingResults=Loading results diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 7bc8ddcb72..c44b4e2479 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -27,6 +27,7 @@ import javax.swing.JPanel; import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; @@ -92,9 +93,9 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon // - The central repo is enabled and the node has correlatable content // (either through the MD5 hash of the associated file or through a BlackboardArtifact) // - The central repo is disabled and the backing file has a valid MD5 hash - AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); + AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node); if (CentralRepository.isEnabled()) { - return !OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file).isEmpty(); + return !OtherOccurrences.getCorrelationAttributesFromNode(node, file).isEmpty(); } else { return file != null && file.getSize() > 0 diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OccurrencePanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OccurrencePanel.java index c66d4ce085..4fb8ccff35 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OccurrencePanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OccurrencePanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; import java.awt.Color; import java.awt.Font; import java.util.ArrayList; @@ -50,7 +51,7 @@ final class OccurrencePanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; private int gridY = 0; - private final List nodeDataList; + private final List nodeDataList; private final Map caseNamesAndDates = new HashMap<>(); private final Set dataSourceNames = new HashSet<>(); private final Set filePaths = new HashSet<>(); @@ -97,7 +98,7 @@ final class OccurrencePanel extends javax.swing.JPanel { * @param nodeDataList the list of OtherOccurrenceNodeData representing * common properties for the file */ - OccurrencePanel(List nodeDataList) { + OccurrencePanel(List nodeDataList) { this.nodeDataList = nodeDataList; customizeComponents(); } @@ -148,9 +149,9 @@ final class OccurrencePanel extends javax.swing.JPanel { addItemToBag(gridY, 0, TOP_INSET, 0, commonPropertiesLabel); gridY++; //for each other occurrence - for (OtherOccurrenceNodeData occurrence : nodeDataList) { - if (occurrence instanceof OtherOccurrenceNodeInstanceData) { - String type = ((OtherOccurrenceNodeInstanceData) occurrence).getType(); + for (NodeData occurrence : nodeDataList) { + if (occurrence instanceof NodeData) { + String type = occurrence.getType(); if (!type.isEmpty()) { javax.swing.JLabel typeLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(typeLabel, Bundle.OccurrencePanel_commonPropertyTypeLabel_text()); @@ -160,7 +161,7 @@ final class OccurrencePanel extends javax.swing.JPanel { addItemToBag(gridY, 1, VERTICAL_GAP, 0, typeFieldValue); gridY++; } - String value = ((OtherOccurrenceNodeInstanceData) occurrence).getValue(); + String value = occurrence.getValue(); if (!value.isEmpty()) { javax.swing.JLabel valueLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(valueLabel, Bundle.OccurrencePanel_commonPropertyValueLabel_text()); @@ -170,7 +171,7 @@ final class OccurrencePanel extends javax.swing.JPanel { addItemToBag(gridY, 1, 0, 0, valueFieldValue); gridY++; } - TskData.FileKnown knownStatus = ((OtherOccurrenceNodeInstanceData) occurrence).getKnown(); + TskData.FileKnown knownStatus = occurrence.getKnown(); javax.swing.JLabel knownStatusLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text()); addItemToBag(gridY, 0, 0, 0, knownStatusLabel); @@ -181,7 +182,7 @@ final class OccurrencePanel extends javax.swing.JPanel { } addItemToBag(gridY, 1, 0, 0, knownStatusValue); gridY++; - String comment = ((OtherOccurrenceNodeInstanceData) occurrence).getComment(); + String comment = occurrence.getComment(); if (!comment.isEmpty()) { javax.swing.JLabel commentLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(commentLabel, Bundle.OccurrencePanel_commonPropertyCommentLabel_text()); @@ -201,10 +202,9 @@ final class OccurrencePanel extends javax.swing.JPanel { } String caseDate = ""; try { - OtherOccurrenceNodeInstanceData nodeData = ((OtherOccurrenceNodeInstanceData) occurrence); - if (nodeData.isCentralRepoNode()) { + if (occurrence.isCentralRepoNode()) { if (CentralRepository.isEnabled()) { - CorrelationCase partialCase = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); + CorrelationCase partialCase = occurrence.getCorrelationAttributeInstance().getCorrelationCase(); caseDate = CentralRepository.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate(); } } else { @@ -214,9 +214,9 @@ final class OccurrencePanel extends javax.swing.JPanel { LOGGER.log(Level.WARNING, "Error getting case created date for other occurrence content viewer", ex); } //Collect the data that is necessary for the other sections - caseNamesAndDates.put(((OtherOccurrenceNodeInstanceData) occurrence).getCaseName(), caseDate); - dataSourceNames.add(((OtherOccurrenceNodeInstanceData) occurrence).getDataSourceName()); - filePaths.add(((OtherOccurrenceNodeInstanceData) occurrence).getFilePath()); + caseNamesAndDates.put(occurrence.getCaseName(), caseDate); + dataSourceNames.add(occurrence.getDataSourceName()); + filePaths.add(occurrence.getFilePath()); } } //end for each diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java deleted file mode 100644 index da4915e75f..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 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.centralrepository.contentviewer; -/** - * Marker interface for Other Occurrences nodes. - */ -interface OtherOccurrenceNodeData { - -} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java deleted file mode 100755 index 99e530349a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 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.centralrepository.contentviewer; - -/** - * Class for populating the Other Occurrences tab with a single message. - */ -final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData { - private final String displayMessage; - - OtherOccurrenceNodeMessageData(String displayMessage) { - this.displayMessage = displayMessage; - } - - String getDisplayMessage() { - return displayMessage; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java index e8e2da1939..9fd7b7fa51 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceOneTypeWorker.java @@ -30,6 +30,9 @@ import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; +import org.sleuthkit.autopsy.centralrepository.application.UniquePathKey; import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrenceOneTypeWorker.OneTypeData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; @@ -77,13 +80,13 @@ class OtherOccurrenceOneTypeWorker extends SwingWorker { int totalCount = 0; Set dataSources = new HashSet<>(); Collection correlationAttributesToAdd = new ArrayList<>(); - String earliestDate = OtherOccurrenceUtilities.getEarliestCaseDate(); + String earliestDate = OtherOccurrences.getEarliestCaseDate(); OneTypeData results = null; if (CentralRepository.isEnabled()) { List instances; instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value); - HashMap nodeDataMap = new HashMap<>(); + HashMap nodeDataMap = new HashMap<>(); String caseUUID = Case.getCurrentCase().getName(); for (CorrelationAttributeInstance artifactInstance : instances) { if (isCancelled()) { @@ -104,26 +107,26 @@ class OtherOccurrenceOneTypeWorker extends SwingWorker { continue; } correlationAttributesToAdd.add(artifactInstance); - OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, aType, value); + NodeData newNode = new NodeData(artifactInstance, aType, value); UniquePathKey uniquePathKey = new UniquePathKey(newNode); nodeDataMap.put(uniquePathKey, newNode); } - for (OtherOccurrenceNodeInstanceData nodeData : nodeDataMap.values()) { + for (NodeData nodeData : nodeDataMap.values()) { if (isCancelled()) { break; } if (nodeData.isCentralRepoNode()) { try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); } catch (CentralRepoException ex) { logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); } } else { try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "No current case open for other occurrences", ex); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesDataSourcesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesDataSourcesTableModel.java index c84b00fc80..55e1dea428 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesDataSourcesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesDataSourcesTableModel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -26,6 +27,7 @@ import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -139,11 +141,10 @@ final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel { * * @param newNodeData data to add to the table */ - void addNodeData(OtherOccurrenceNodeData newNodeData) { - OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) newNodeData; + void addNodeData(NodeData newNodeData) { String caseUUID; try { - caseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(); + caseUUID = newNodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(); } catch (CentralRepoException ignored) { //non central repo nodeData won't have a correlation case try { @@ -151,10 +152,10 @@ final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel { //place holder value will be used since correlation attribute was unavailble } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Unable to get current case", ex); - caseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); + caseUUID = OtherOccurrences.getPlaceholderUUID(); } } - dataSourceSet.add(new DataSourceColumnItem(nodeData.getCaseName(), nodeData.getDeviceID(), nodeData.getDataSourceName(), caseUUID)); + dataSourceSet.add(new DataSourceColumnItem(newNodeData.getCaseName(), newNodeData.getDeviceID(), newNodeData.getDataSourceName(), caseUUID)); fireTableDataChanged(); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java index 74a64c1e41..c64acaaa4f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -28,6 +29,7 @@ import org.openide.util.NbBundle.Messages; import org.apache.commons.io.FilenameUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -40,7 +42,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(OtherOccurrencesFilesTableModel.class.getName()); private final List nodeKeys = new ArrayList<>(); - private final Map> nodeMap = new HashMap<>(); + private final Map> nodeMap = new HashMap<>(); /** * Create a table model for displaying file names @@ -75,7 +77,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { || nodeMap.get(nodeKeys.get(rowIdx)).isEmpty()) { return Bundle.OtherOccurrencesFilesTableModel_noData(); } - return FilenameUtils.getName(((OtherOccurrenceNodeInstanceData) nodeMap.get(nodeKeys.get(rowIdx)).get(0)).getFilePath()); + return FilenameUtils.getName( nodeMap.get(nodeKeys.get(rowIdx)).get(0).getFilePath()); } /** @@ -87,7 +89,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { * @return a list of OtherOccurrenceNodeData for the specified index or an * empty list if no data was found */ - List getListOfNodesForFile(int rowIdx) { + List getListOfNodesForFile(int rowIdx) { //if anything would prevent this from working return an empty list if (nodeMap.isEmpty() || nodeKeys.isEmpty() || rowIdx < 0 || rowIdx >= nodeKeys.size() || nodeKeys.get(rowIdx) == null @@ -107,9 +109,9 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { * * @param newNodeData data to add to the table */ - void addNodeData(OtherOccurrenceNodeData newNodeData) { - String newNodeKey = createNodeKey((OtherOccurrenceNodeInstanceData) newNodeData);//FilenameUtils.getName(((OtherOccurrenceNodeInstanceData)newNodeData).getFilePath()); - List nodeList = nodeMap.get(newNodeKey); + void addNodeData(NodeData newNodeData) { + String newNodeKey = createNodeKey((NodeData) newNodeData);//FilenameUtils.getName(((OtherOccurrenceNodeInstanceData)newNodeData).getFilePath()); + List nodeList = nodeMap.get(newNodeKey); if (nodeList == null) { nodeKeys.add(newNodeKey); nodeList = new ArrayList<>(); @@ -119,7 +121,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { fireTableDataChanged(); } - private String createNodeKey(OtherOccurrenceNodeInstanceData nodeData) { + private String createNodeKey(NodeData nodeData) { String caseUUID; try { caseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(); @@ -130,7 +132,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { //place holder value will be used since correlation attribute was unavailble } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Unable to get current case", ex); - caseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); + caseUUID = OtherOccurrences.getPlaceholderUUID(); } } return nodeData.getCaseName() + nodeData.getDataSourceName() + nodeData.getDeviceID() + nodeData.getFilePath() + caseUUID; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java index 7b317f86ee..0654002f6f 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesNodeWorker.java @@ -28,6 +28,8 @@ import javax.swing.SwingWorker; import org.openide.nodes.Node; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -58,7 +60,7 @@ class OtherOccurrencesNodeWorker extends SwingWorker @Override protected OtherOccurrencesData doInBackground() throws Exception { - AbstractFile file = OtherOccurrenceUtilities.getAbstractFileFromNode(node); + AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node); String deviceId = ""; String dataSourceName = ""; Map caseNames = new HashMap<>(); @@ -75,22 +77,22 @@ class OtherOccurrencesNodeWorker extends SwingWorker // @@@ Review this behavior return null; } - Collection correlationAttributes = OtherOccurrenceUtilities.getCorrelationAttributesFromNode(node, file); + Collection correlationAttributes = OtherOccurrences.getCorrelationAttributesFromNode(node, file); int totalCount = 0; Set dataSources = new HashSet<>(); for (CorrelationAttributeInstance corAttr : correlationAttributes) { - for (OtherOccurrenceNodeInstanceData nodeData : OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr).values()) { + for (NodeData nodeData : OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr).values()) { if (nodeData.isCentralRepoNode()) { try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase()); } catch (CentralRepoException ex) { logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex); } } else { try { - dataSources.add(OtherOccurrenceUtilities.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); + dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName())); caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName())); } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "No current case open for other occurrences", ex); @@ -105,7 +107,7 @@ class OtherOccurrencesNodeWorker extends SwingWorker } if (!isCancelled()) { - data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrenceUtilities.getEarliestCaseDate()); + data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrences.getEarliestCaseDate()); } return data; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java index 02bee6362f..97552d8091 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java @@ -18,6 +18,9 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import org.sleuthkit.autopsy.centralrepository.application.NodeData; +import org.sleuthkit.autopsy.centralrepository.application.UniquePathKey; +import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -68,7 +71,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { private static final CorrelationCaseWrapper NO_ARTIFACTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noArtifacts()); private static final CorrelationCaseWrapper NO_RESULTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noResultsFound()); private static final CorrelationCaseWrapper LOADING_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_loadingResults()); - private static final String UUID_PLACEHOLDER_STRING = "NoCorrelationAttributeInstance"; private static final Logger logger = Logger.getLogger(OtherOccurrencesPanel.class.getName()); private static final long serialVersionUID = 1L; private final OtherOccurrencesFilesTableModel filesTableModel; @@ -93,15 +95,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { customizeComponents(); } - /** - * Get a placeholder string to use in place of case uuid when it isn't - * available - * - * @return UUID_PLACEHOLDER_STRING - */ - static String getPlaceholderUUID() { - return UUID_PLACEHOLDER_STRING; - } private void customizeComponents() { ActionListener actList = (ActionEvent e) -> { @@ -223,21 +216,17 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { if (-1 != selectedRowViewIdx) { CentralRepository dbManager = CentralRepository.getInstance(); int selectedRowModelIdx = filesTable.convertRowIndexToModel(selectedRowViewIdx); - List rowList = filesTableModel.getListOfNodesForFile(selectedRowModelIdx); + List rowList = filesTableModel.getListOfNodesForFile(selectedRowModelIdx); if (!rowList.isEmpty()) { - if (rowList.get(0) instanceof OtherOccurrenceNodeInstanceData) { - CorrelationCase eamCasePartial = ((OtherOccurrenceNodeInstanceData) rowList.get(0)).getCorrelationAttributeInstance().getCorrelationCase(); - caseDisplayName = eamCasePartial.getDisplayName(); - // query case details - CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID()); - if (eamCase != null) { - details = eamCase.getCaseDetailsOptionsPaneDialog(); - } else { - details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetails(); - } + CorrelationCase eamCasePartial = rowList.get(0).getCorrelationAttributeInstance().getCorrelationCase(); + caseDisplayName = eamCasePartial.getDisplayName(); + // query case details + CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID()); + if (eamCase != null) { + details = eamCase.getCaseDetailsOptionsPaneDialog(); } else { - details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_notSelected(); - } + details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetails(); + } } else { details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetailsReference(); } @@ -272,18 +261,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } } } - - @NbBundle.Messages({ - "OtherOccurrencesPanel.csvHeader.case=Case", - "OtherOccurrencesPanel.csvHeader.device=Device", - "OtherOccurrencesPanel.csvHeader.dataSource=Data Source", - "OtherOccurrencesPanel.csvHeader.attribute=Matched Attribute", - "OtherOccurrencesPanel.csvHeader.value=Attribute Value", - "OtherOccurrencesPanel.csvHeader.known=Known", - "OtherOccurrencesPanel.csvHeader.path=Path", - "OtherOccurrencesPanel.csvHeader.comment=Comment" - }) - + + @NbBundle.Messages({"OtherOccurrencesPanel_earliestCaseNotAvailable=Not Availble."}) /** * Reset the UI and clear cached data. */ @@ -330,7 +309,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } else if (caseCount == 0) { casesTableModel.addCorrelationCase(NO_RESULTS_CASE); } - earliestCaseDate.setText(data.getEarliestCaseDate()); + String earliestDate = data.getEarliestCaseDate(); + earliestCaseDate.setText(earliestDate.isEmpty() ? Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable() : earliestDate); foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getTotalCount(), caseCount, data.getDataSourceCount())); if (caseCount > 0) { casesTable.setRowSelectionInterval(0, 0); @@ -379,7 +359,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } else if (caseCount == 0) { casesTableModel.addCorrelationCase(NO_RESULTS_CASE); } - earliestCaseDate.setText(data.getEarliestCaseDate()); + String earliestDate = data.getEarliestCaseDate(); + earliestCaseDate.setText(earliestDate.isEmpty() ? Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable() : earliestDate); foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getInstanceDataCount(), caseCount, data.getDataSourceCount())); if (caseCount > 0) { casesTable.setRowSelectionInterval(0, 0); @@ -410,11 +391,11 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex); } for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); + for (NodeData nodeData : correlatedNodeDataMap.values()) { for (int selectedRow : selectedCaseIndexes) { try { if (nodeData.isCentralRepoNode()) { @@ -450,11 +431,11 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { int[] selectedDataSources = dataSourcesTable.getSelectedRows(); filesTableModel.clearTable(); for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(OtherOccurrenceUtilities.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); - for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { + correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); + for (NodeData nodeData : correlatedNodeDataMap.values()) { for (int selectedDataSourceRow : selectedDataSources) { try { if (nodeData.isCentralRepoNode()) { @@ -585,7 +566,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { @Override protected Void doInBackground() throws Exception { - OtherOccurrenceUtilities.writeOtherOccurrencesToFileAsCSV(this.destFile, this.abstractFile, this.correlationAttList, this.dataSourceName, this.deviceId); + OtherOccurrences.writeOtherOccurrencesToFileAsCSV(this.destFile, this.abstractFile, this.correlationAttList, this.dataSourceName, this.deviceId); return null; } @@ -778,9 +759,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { boolean enableCentralRepoActions = false; if (CentralRepository.isEnabled() && filesTable.getSelectedRowCount() == 1) { int rowIndex = filesTable.getSelectedRow(); - List selectedFile = filesTableModel.getListOfNodesForFile(rowIndex); - if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof OtherOccurrenceNodeInstanceData) { - OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedFile.get(0); + List selectedFile = filesTableModel.getListOfNodesForFile(rowIndex); + if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof NodeData) { + NodeData instanceData = selectedFile.get(0); enableCentralRepoActions = instanceData.isCentralRepoNode(); } } From a549951eb39a502a8d3ab3ef063422652a5320b4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 17 May 2021 13:56:11 -0400 Subject: [PATCH 22/78] initial changes --- .../autopsy/datamodel/AbstractContentNode.java | 14 ++++++++++++-- .../org/sleuthkit/autopsy/datamodel/ImageNode.java | 14 -------------- .../sleuthkit/autopsy/datamodel/OsAccounts.java | 5 ----- .../org/sleuthkit/autopsy/datamodel/PoolNode.java | 14 -------------- .../autopsy/datamodel/UnsupportedContentNode.java | 14 -------------- .../sleuthkit/autopsy/datamodel/VolumeNode.java | 14 -------------- 6 files changed, 12 insertions(+), 63 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 8b1d22d349..e551a62b14 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; @@ -57,7 +58,7 @@ public abstract class AbstractContentNode extends ContentNode /** * Underlying Sleuth Kit Content object */ - T content; + protected final T content; private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName()); /** @@ -339,7 +340,16 @@ public abstract class AbstractContentNode extends ContentNode * * @return Score property for the underlying content of the node. */ - abstract protected Pair getScorePropertyAndDescription(List tags); + protected Pair getScorePropertyAndDescription(List tags) { + Score score = null; + try { + score = this.content.getAggregateScore(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get aggregate score for content with id: " + this.content.getId(), ex); + } + + + } /** * Returns comment property for the node. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 06a50eb788..ea0e2bb347 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -282,20 +282,6 @@ public class ImageNode extends AbstractContentNode { return null; } - /** - * Returns Score property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags. - * - * @return Score property for the underlying content of the node. - */ - @Override - protected Pair getScorePropertyAndDescription(List tags) { - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } - /** * Returns comment property for the node. * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 170251bd13..be36f0a062 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -333,11 +333,6 @@ public final class OsAccounts implements AutopsyVisitableItem { return null; } - @Override - protected Pair getScorePropertyAndDescription(List tags) { - return null; - } - @Override protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { return DataResultViewerTable.HasCommentStatus.NO_COMMENT; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java index 9bc7736952..3050e25712 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/PoolNode.java @@ -156,20 +156,6 @@ public class PoolNode extends AbstractContentNode { return null; } - /** - * Returns Score property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags. - * - * @return Score property for the underlying content of the node. - */ - @Override - protected Pair getScorePropertyAndDescription(List tags) { - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } - /** * Returns comment property for the node. * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/UnsupportedContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/UnsupportedContentNode.java index 4d8a3473e6..6d4b9fe5c6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/UnsupportedContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/UnsupportedContentNode.java @@ -138,20 +138,6 @@ public class UnsupportedContentNode extends AbstractContentNode getScorePropertyAndDescription(List tags) { - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } - /** * Returns comment property for the node. * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index f8a6f2e5df..709c8375a9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -258,20 +258,6 @@ public class VolumeNode extends AbstractContentNode { return null; } - /** - * Returns Score property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags. - * - * @return Score property for the underlying content of the node. - */ - @Override - protected Pair getScorePropertyAndDescription(List tags) { - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } - /** * Returns comment property for the node. * From d69d7216b51e7030a4de11f6857aa6b2ca28eb08 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 17 May 2021 16:13:14 -0400 Subject: [PATCH 23/78] first draft; description needs to be done --- .../corecomponents/DataResultViewerTable.java | 40 +++++--- .../datamodel/AbstractAbstractFileNode.java | 41 +------- .../datamodel/AbstractContentNode.java | 7 +- .../datamodel/BlackboardArtifactNode.java | 94 +------------------ .../sleuthkit/autopsy/datamodel/SCOData.java | 7 +- 5 files changed, 38 insertions(+), 151 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 48e7427172..8464969920 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -87,6 +87,8 @@ import org.sleuthkit.autopsy.datamodel.BaseChildFactory; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.Score.Significance; /** * A tabular result viewer that displays the children of the given root node @@ -1263,6 +1265,29 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private static final long serialVersionUID = 1L; + /** + * Returns the icon denoted by the Score's Significance. + * @param significance The Score's Significance. + * @return The icon (or null) related to that significance. + */ + private ImageIcon getIcon(Significance significance) { + if (significance == null) { + return null; + } + + switch (significance) { + case NOTABLE: + return NOTABLE_ICON_SCORE; + case LIKELY_NOTABLE: + return INTERESTING_SCORE_ICON; + case LIKELY_NONE: + case NONE: + case UNKNOWN: + default: + return null; + } + } + @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); @@ -1283,19 +1308,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { switchValue = value; } setText(""); - if ((switchValue instanceof Score)) { - - switch ((Score) switchValue) { - case INTERESTING_SCORE: - setIcon(INTERESTING_SCORE_ICON); - break; - case NOTABLE_SCORE: - setIcon(NOTABLE_ICON_SCORE); - break; - case NO_SCORE: - default: - setIcon(null); - } + if ((switchValue instanceof org.sleuthkit.datamodel.Score)) { + setIcon(getIcon(((org.sleuthkit.datamodel.Score) switchValue).getSignificance())); } else { setIcon(null); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index ee862eee96..2bd4e702c3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -46,7 +46,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; @@ -59,14 +58,13 @@ import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.texttranslation.TranslationException; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil; +import org.sleuthkit.datamodel.Score; /** * An abstract node that encapsulates AbstractFile data @@ -432,43 +430,6 @@ public abstract class AbstractAbstractFileNode extends A return Pair.of(count, description); } - @NbBundle.Messages({ - "AbstractAbstractFileNode.createSheet.score.displayName=S", - "AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.", - "AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.", - "AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.", - "AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.", - "AbstractAbstractFileNode.createSheet.noScore.description=No score"}) - @Override - protected Pair getScorePropertyAndDescription(List tags) { - DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE; - String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description(); - if (content.getKnown() == TskData.FileKnown.BAD) { - score = DataResultViewerTable.Score.NOTABLE_SCORE; - description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description(); - } - try { - if (score == DataResultViewerTable.Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) { - score = DataResultViewerTable.Score.INTERESTING_SCORE; - description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description(); - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error getting artifacts for file: " + content.getName(), ex); - } - if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) { - score = DataResultViewerTable.Score.INTERESTING_SCORE; - description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description(); - for (Tag tag : tags) { - if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { - score = DataResultViewerTable.Score.NOTABLE_SCORE; - description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description(); - break; - } - } - } - return Pair.of(score, description); - } - @NbBundle.Messages({ "AbstractAbstractFileNode.createSheet.comment.displayName=C"}) @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index e551a62b14..ee9d4a425e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -340,15 +340,16 @@ public abstract class AbstractContentNode extends ContentNode * * @return Score property for the underlying content of the node. */ - protected Pair getScorePropertyAndDescription(List tags) { - Score score = null; + protected Pair getScorePropertyAndDescription(List tags) { + Score score = Score.SCORE_UNKNOWN; try { score = this.content.getAggregateScore(); } catch (TskCoreException ex) { logger.log(Level.WARNING, "Unable to get aggregate score for content with id: " + this.content.getId(), ex); } - + score.getSignificance().getDisplayName(); + return Pair.of(score, ); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 5bfca75425..8e52e6d8b7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -59,12 +59,10 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUti import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; -import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; @@ -75,12 +73,12 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.datamodel.utils.IconsUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; +import org.sleuthkit.datamodel.Score; /** * A BlackboardArtifactNode is an AbstractNode implementation that can be used @@ -843,94 +841,6 @@ public class BlackboardArtifactNode extends AbstractContentNode getScorePropertyAndDescription(List tags) { - /* - * Is the artifact's source content marked as notable? - */ - Score score = Score.NO_SCORE; - String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description(); - if (srcContent instanceof AbstractFile) { - if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) { - score = Score.NOTABLE_SCORE; - description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description(); - } - } - - /* - * If the artifact is a hash set hit, is the hash set a notable hashes - * hash set? - */ - if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - try { - BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME)); - List notableHashsets = HashDbManager.getInstance().getKnownBadFileHashSets(); - for (HashDbManager.HashDb hashDb : notableHashsets) { - if (hashDb.getHashSetName().equals(attr.getValueString())) { - score = Score.NOTABLE_SCORE; - description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description(); - break; - } - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute for TSK_HASHSET_HIT artifact (artifact objID={0})", artifact.getId()), ex); - } - } - - /* - * Is the artifact's source content notable? - */ - if (score == Score.NO_SCORE) { - try { - if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) { - score = Score.INTERESTING_SCORE; - description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description(); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts for source content (artifact objID={0})", artifact.getId()), ex); - } - } - - /* - * Analyze any tags applied to the artifact or its source content. If - * there are tags, tha artifact is at least interesting. If one of the - * tags is a notable tag, the artifact is notable. - */ - if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) { - score = Score.INTERESTING_SCORE; - description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description(); - for (Tag tag : tags) { - if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { - score = Score.NOTABLE_SCORE; - description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description(); - break; - } - } - } - - return Pair.of(score, description); - } - /** * Computes the value of the other occurrences property ("O" in S, C, O) for * the artifact represented by this node. The value of the other occurrences @@ -1146,7 +1056,7 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) { - Pair scoreAndDescription = getScorePropertyAndDescription(tags); + Pair scoreAndDescription = getScorePropertyAndDescription(tags); sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java b/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java index a9dd99369d..ed9d232034 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel; import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.datamodel.Score; /** * Container to bag the S C & O data for an abstract file node. @@ -27,11 +28,11 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; */ class SCOData { - private Pair scoreAndDescription = null; + private Pair scoreAndDescription = null; private DataResultViewerTable.HasCommentStatus comment = null; private Pair countAndDescription = null; - Pair getScoreAndDescription() { + Pair getScoreAndDescription() { return scoreAndDescription; } @@ -43,7 +44,7 @@ class SCOData { return countAndDescription; } - void setScoreAndDescription(Pair scoreAndDescription) { + void setScoreAndDescription(Pair scoreAndDescription) { this.scoreAndDescription = scoreAndDescription; } void setComment(DataResultViewerTable.HasCommentStatus comment) { From 711095725256430ee7aebe1375fdad76ffc07efd Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 18 May 2021 08:55:22 -0400 Subject: [PATCH 24/78] updates in Discovery --- .../corecomponents/DataResultViewerTable.java | 1 - .../datamodel/AbstractContentNode.java | 10 ++- .../datamodel/Bundle.properties-MERGED | 8 +-- .../discovery/search/Bundle.properties-MERGED | 6 +- .../autopsy/discovery/search/ResultFile.java | 67 +++++-------------- .../discovery/ui/DiscoveryUiUtils.java | 35 ++++++---- 6 files changed, 50 insertions(+), 77 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 8464969920..e8c5ae4aea 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -87,7 +87,6 @@ import org.sleuthkit.autopsy.datamodel.BaseChildFactory; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent; -import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.Score.Significance; /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index ee9d4a425e..bf9a7669c1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -32,6 +32,7 @@ import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -340,6 +341,10 @@ public abstract class AbstractContentNode extends ContentNode * * @return Score property for the underlying content of the node. */ + @Messages({ + "# {0} - significanceDisplayName", + "AbstractContentNode_getScorePropertyAndDescription_description=Has an {0} analysis result score" + }) protected Pair getScorePropertyAndDescription(List tags) { Score score = Score.SCORE_UNKNOWN; try { @@ -348,8 +353,9 @@ public abstract class AbstractContentNode extends ContentNode logger.log(Level.WARNING, "Unable to get aggregate score for content with id: " + this.content.getId(), ex); } - score.getSignificance().getDisplayName(); - return Pair.of(score, ); + String significanceDisplay = score.getSignificance().getDisplayName(); + String description = Bundle.AbstractContentNode_getScorePropertyAndDescription_description(significanceDisplay); + return Pair.of(score, description); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 2feefb56c7..3aa5e90bd3 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -9,13 +9,7 @@ AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource AbstractAbstractFileNode.createSheet.count.displayName=O AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated AbstractAbstractFileNode.createSheet.count.name=O -AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it. -AbstractAbstractFileNode.createSheet.noScore.description=No score -AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable. -AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag. -AbstractAbstractFileNode.createSheet.score.displayName=S AbstractAbstractFileNode.createSheet.score.name=S -AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged. AbstractAbstractFileNode.extensionColLbl=Extension AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir) AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta) @@ -38,6 +32,8 @@ AbstractAbstractFileNode.typeMetaColLbl=Type(Meta) AbstractAbstractFileNode.useridColLbl=UserID AbstractContentNode.nodescription=no description AbstractContentNode.valueLoading=value loading +# {0} - significanceDisplayName +AbstractContentNode_getScorePropertyAndDescription_description=Has an {0} analysis result score AbstractFsContentNode.noDesc.text=no description AnalysisResults_name=Analysis Results ArtifactStringContent.attrsTableHeader.sources=Source(s) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED index ac2756ea33..2115f5256b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED @@ -58,10 +58,8 @@ FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names FileSorter.SortingMethod.pageViews.displayName=Page Views ResultDomain_getDefaultCategory=Uncategorized ResultDomain_noAccountTypes=Unknown -ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it. -ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable. -ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag. -ResultFile.score.taggedFile.description=At least one instance of the file has been tagged. +# {0} - significanceDisplayName +ResultFile_updateScoreAndDescription_description=Has an {0} analysis result score SearchData.AttributeType.Domain.displayName=Domain SearchData.FileSize.100kbto1mb=: 100KB-1MB SearchData.FileSize.100mbto1gb=: 100MB-1GB diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultFile.java b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultFile.java index 59bfbf48f2..24237bed64 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultFile.java @@ -23,18 +23,15 @@ import org.sleuthkit.datamodel.AbstractFile; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.OTHER; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.HashUtility; -import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -49,7 +46,7 @@ public class ResultFile extends Result { private final List interestingSetNames; private final List objectDetectedNames; private final List instances = new ArrayList<>(); - private DataResultViewerTable.Score currentScore = DataResultViewerTable.Score.NO_SCORE; + private Score currentScore = Score.SCORE_UNKNOWN; private String scoreDescription = null; private boolean deleted = false; private Type fileType; @@ -108,7 +105,7 @@ public class ResultFile extends Result { * * @return The score of this ResultFile. */ - public DataResultViewerTable.Score getScore() { + public Score getScore() { return currentScore; } @@ -286,56 +283,22 @@ public class ResultFile extends Result { } } - /** - * Get all tags from the case database that are associated with the file - * - * @return a list of tags that are associated with the file - */ - private List getContentTagsFromDatabase(AbstractFile file) { - List tags = new ArrayList<>(); - try { - tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(file)); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to get tags for file " + file.getName(), ex); - } - return tags; - } - + @NbBundle.Messages({ - "ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.", - "ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.", - "ResultFile.score.taggedFile.description=At least one instance of the file has been tagged.", - "ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag."}) + "# {0} - significanceDisplayName", + "ResultFile_updateScoreAndDescription_description=Has an {0} analysis result score" + }) private void updateScoreAndDescription(AbstractFile file) { - if (currentScore == DataResultViewerTable.Score.NOTABLE_SCORE) { - //already notable can return - return; - } - if (file.getKnown() == TskData.FileKnown.BAD) { - currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; - scoreDescription = Bundle.ResultFile_score_notableFile_description(); - return; - } + Score score = Score.SCORE_UNKNOWN; try { - if (currentScore == DataResultViewerTable.Score.NO_SCORE && !file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) { - currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; - scoreDescription = Bundle.ResultFile_score_interestingResult_description(); - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error getting artifacts for file: " + file.getName(), ex); - } - List tags = getContentTagsFromDatabase(file); - if (!tags.isEmpty()) { - currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; - scoreDescription = Bundle.ResultFile_score_taggedFile_description(); - for (Tag tag : tags) { - if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { - currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; - scoreDescription = Bundle.ResultFile_score_notableTaggedFile_description(); - return; - } - } + score = Case.getCurrentCaseThrows().getSleuthkitCase().getScoringManager().getAggregateScore(file.getId()); + } catch (NoCurrentCaseException | TskCoreException ex) { + } + + this.currentScore = score; + String significanceDisplay = score.getSignificance().getDisplayName(); + this.scoreDescription = Bundle.ResultFile_updateScoreAndDescription_description(significanceDisplay); } /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryUiUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryUiUtils.java index 7b4287a0b6..e94ca0c1a0 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryUiUtils.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryUiUtils.java @@ -60,6 +60,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; +import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -208,21 +209,31 @@ final class DiscoveryUiUtils { */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) { - switch (resultFile.getScore()) { - case NOTABLE_SCORE: - scoreLabel.setIcon(NOTABLE_SCORE_ICON); - break; - case INTERESTING_SCORE: - scoreLabel.setIcon(INTERESTING_SCORE_ICON); - break; - case NO_SCORE: // empty case - this is interpreted as an intentional fall-through - default: - scoreLabel.setIcon(null); - break; + ImageIcon icon = null; + + Score score = resultFile.getScore(); + if (score != null && score.getSignificance() != null) { + switch (score.getSignificance()) { + case NOTABLE: + icon = NOTABLE_SCORE_ICON; + break; + case LIKELY_NOTABLE: + icon = INTERESTING_SCORE_ICON; + break; + case LIKELY_NONE: + case NONE: + case UNKNOWN: + default: + icon = null; + break; + } } + + scoreLabel.setIcon(icon); scoreLabel.setToolTipText(resultFile.getScoreDescription()); } - + + /** * Get the size of the icons used by the UI. * From 34f156b001fa5e862350d6ae4d28305893240f0e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 20 May 2021 16:57:55 -0400 Subject: [PATCH 25/78] Removed the MessageArtifactViewer db calls from the edt --- .../MessageArtifactViewer.java | 234 ++++++------------ .../MessageArtifactWorker.java | 201 +++++++++++++++ 2 files changed, 280 insertions(+), 155 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactWorker.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java index 812fb7290d..d72f6052b8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java @@ -22,18 +22,15 @@ import org.sleuthkit.autopsy.datamodel.AttachmentNode; import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; +import java.awt.Cursor; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JScrollPane; import javax.swing.text.JTextComponent; import org.apache.commons.lang3.StringUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -44,13 +41,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.contentviewers.TranslatablePanel; import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException; import org.sleuthkit.autopsy.contentviewers.Utilities; +import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactWorker.MesssageArtifactData; import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; @@ -71,13 +68,8 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; -import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments; -import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment; import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Attachment; -import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.URLAttachment; /** * Shows SMS/MMS/EMail messages @@ -141,6 +133,8 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac private ExplorerManager drpExplorerManager; private MessageAccountPanel accountsPanel; + + private MessageArtifactWorker worker; public MessageArtifactViewer(List textAreas, DataResultPanel drp) { this.textAreas = textAreas; @@ -419,54 +413,47 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac @Override public void setArtifact(BlackboardArtifact artifact) { - this.artifact = artifact; + resetComponent(); + + if (worker != null) { + worker.cancel(true); + worker = null; + } + if (artifact == null) { - resetComponent(); return; } - /* - * If the artifact is a keyword hit, use the associated artifact as the - * one to show in this viewer - */ - if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { - try { - getAssociatedArtifact(artifact).ifPresent(associatedArtifact -> { - this.artifact = associatedArtifact; - }); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); + worker = new MessageArtifactWorker(artifact) { + @Override + public void done() { + if (isCancelled()) { + return; + } + + try { + MesssageArtifactData data = get(); + MessageArtifactViewer.this.artifact = data.getArtifact(); + if (data.getArtifact().getArtifactTypeID() == TSK_MESSAGE.getTypeID()) { + displayMsg(data); + } else if (data.getArtifact().getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) { + displayEmailMsg(data); + } else { + resetComponent(); + } + + msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true); + accountsPanel.setArtifact(data.getArtifact()); + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, String.format("Failed to update message viewer for artifact (%d)", artifact.getId(), ex)); + } } - } + }; - if (this.artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()) { - displayMsg(); - } else if (this.artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) { - displayEmailMsg(); - } else { - resetComponent(); - } - - msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true); - accountsPanel.setArtifact(artifact); - } - - /** - * Get the artifact associated with the given artifact, if there is one. - * - * @param artifact The artifact to get the associated artifact from. Must - * not be null - * - * @throws TskCoreException If there is a critical error querying the DB. - * @return An optional containing the artifact associated with the given - * artifact, if there is one. - */ - private static Optional getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException { - BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE); - if (attribute != null) { - return Optional.of(artifact.getSleuthkitCase().getArtifactByArtifactId(attribute.getValueLong())); - } - return Optional.empty(); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + worker.execute(); } @Override @@ -505,7 +492,7 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac //if the artifact is a keyword hit, check if its associated artifact is a message or email. if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { try { - if (getAssociatedArtifact(artifact).map(MessageArtifactViewer::isMessageArtifact).orElse(false)) { + if (MessageArtifactWorker.getAssociatedArtifact(artifact).map(MessageArtifactViewer::isMessageArtifact).orElse(false)) { return true; } } catch (TskCoreException ex) { @@ -534,27 +521,25 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac * Configure the text area at the given index to show the content of the * given type. * - * @param type The ATTRIBUT_TYPE to show in the indexed tab. + * @param text text to show in the indexed tab. * @param index The index of the text area to configure. * * @throws TskCoreException */ - private void configureTextArea(BlackboardAttribute.ATTRIBUTE_TYPE type, int index) throws TskCoreException { - String attributeText = getAttributeValueSafe(artifact, type); - - if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) { - htmlPanel.setHtmlText(attributeText); - } else if (index == TEXT_TAB_INDEX && StringUtils.isNotBlank(attributeText)) { - textPanel.setContent(attributeText, artifact.toString()); + private void configureTextArea(String text, int index) { + if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(text)) { + htmlPanel.setHtmlText(text); + } else if (index == TEXT_TAB_INDEX && StringUtils.isNotBlank(text)) { + textPanel.setContent(text, artifact.toString()); } else { JTextComponent textComponent = textAreas.get(index); if (textComponent != null) { - textComponent.setText(attributeText); + textComponent.setText(text); textComponent.setCaretPosition(0); //make sure we start at the top } } - final boolean hasText = attributeText.length() > 0; + final boolean hasText = text.length() > 0; msgbodyTabbedPane.setEnabledAt(index, hasText); if (hasText) { @@ -570,38 +555,8 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac datetimeText.setEnabled(true); } - private void configureAttachments() throws TskCoreException { - - final Set attachments; - - // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute - BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); - if (attachmentsAttr != null) { - - attachments = new HashSet<>(); - try { - MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class); - Collection fileAttachments = msgAttachments.getFileAttachments(); - for (FileAttachment fileAttachment : fileAttachments) { - attachments.add(fileAttachment); - } - Collection urlAttachments = msgAttachments.getUrlAttachments(); - for (URLAttachment urlAttachment : urlAttachments) { - attachments.add(urlAttachment); - } - } catch (BlackboardJsonAttrUtil.InvalidJsonException ex) { - LOGGER.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", artifact.getName()), ex); - } - } else { // For backward compatibility - email attachements are derived files and children of the email message artifact - attachments = new HashSet<>(); - for (Content child : artifact.getChildren()) { - if (child instanceof AbstractFile) { - attachments.add(new FileAttachment((AbstractFile) child)); - } - } - } - - final int numberOfAttachments = attachments.size(); + private void configureAttachments(Set attachments) { + int numberOfAttachments = attachments.size(); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")"); @@ -609,79 +564,48 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac new AttachmentsChildren(attachments))), true)); } - private void displayEmailMsg() { + private void displayEmailMsg(MesssageArtifactData artifactData) { enableCommonFields(); directionText.setEnabled(false); ccLabel.setEnabled(true); - try { - this.fromText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM)); - this.fromText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM)); - this.toText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_TO)); - this.toText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_TO)); - this.directionText.setText(""); - this.ccText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_CC)); - this.ccText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_CC)); - this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT)); - this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME_RCVD)); + this.fromText.setText(artifactData.getAttributeDisplayString( TSK_EMAIL_FROM)); + this.fromText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_FROM)); + this.toText.setText(artifactData.getAttributeDisplayString(TSK_EMAIL_TO)); + this.toText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_TO)); + this.directionText.setText(""); + this.ccText.setText(artifactData.getAttributeDisplayString(TSK_EMAIL_CC)); + this.ccText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_CC)); + this.subjectText.setText(artifactData.getAttributeDisplayString(TSK_SUBJECT)); + this.datetimeText.setText(artifactData.getAttributeDisplayString(TSK_DATETIME_RCVD)); - configureTextArea(TSK_HEADERS, HDR_TAB_INDEX); - configureTextArea(TSK_EMAIL_CONTENT_PLAIN, TEXT_TAB_INDEX); - configureTextArea(TSK_EMAIL_CONTENT_HTML, HTML_TAB_INDEX); - configureTextArea(TSK_EMAIL_CONTENT_RTF, RTF_TAB_INDEX); - configureAttachments(); - } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "Failed to get attributes for email message.", ex); //NON-NLS - } + configureTextArea(artifactData.getAttributeDisplayString(TSK_HEADERS), HDR_TAB_INDEX); + configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_PLAIN), TEXT_TAB_INDEX); + configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_HTML), HTML_TAB_INDEX); + configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_RTF), RTF_TAB_INDEX); + configureAttachments(artifactData.getAttachements()); } - private void displayMsg() { + private void displayMsg(MesssageArtifactData artifactData) { enableCommonFields(); directionText.setEnabled(true); ccLabel.setEnabled(false); - try { - this.fromText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_FROM)); - this.toText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_TO)); - this.directionText.setText(getAttributeValueSafe(artifact, TSK_DIRECTION)); - this.ccText.setText(""); - this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT)); - this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME)); + this.fromText.setText(artifactData.getAttributeDisplayString(TSK_PHONE_NUMBER_FROM)); + this.toText.setText(artifactData.getAttributeDisplayString(TSK_PHONE_NUMBER_TO)); + this.directionText.setText(artifactData.getAttributeDisplayString(TSK_DIRECTION)); + this.ccText.setText(""); + this.subjectText.setText(artifactData.getAttributeDisplayString(TSK_SUBJECT)); + this.datetimeText.setText(artifactData.getAttributeDisplayString(TSK_DATETIME)); - msgbodyTabbedPane.setEnabledAt(HTML_TAB_INDEX, false); - msgbodyTabbedPane.setEnabledAt(RTF_TAB_INDEX, false); - msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); - msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); - configureTextArea(TSK_TEXT, TEXT_TAB_INDEX); - configureAttachments(); - } catch (TskCoreException ex) { - LOGGER.log(Level.WARNING, "Failed to get attributes for message.", ex); //NON-NLS - } - } - - private static String getAttributeValueSafe(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { - return Optional.ofNullable(artifact.getAttribute(new BlackboardAttribute.Type(type))) - .map(BlackboardAttribute::getDisplayString) - .orElse(""); - } - - /** - * Cleans out input HTML string - * - * @param htmlInString The HTML string to cleanse - * - * @return The cleansed HTML String - */ - static private String cleanseHTML(String htmlInString) { - - Document doc = Jsoup.parse(htmlInString); - - //fix all img tags - doc.select("img[src]").forEach(img -> img.attr("src", "")); - - return doc.html(); + msgbodyTabbedPane.setEnabledAt(HTML_TAB_INDEX, false); + msgbodyTabbedPane.setEnabledAt(RTF_TAB_INDEX, false); + msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); + msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); + configureTextArea(artifactData.getAttributeDisplayString(TSK_TEXT), TEXT_TAB_INDEX); + configureAttachments(artifactData.getAttachements()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactWorker.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactWorker.java new file mode 100755 index 0000000000..14d06dd805 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactWorker.java @@ -0,0 +1,201 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.contentviewers.artifactviewers; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; +import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments; + +/** + * SwingWork for gather the data from the DB needed for showing messages. + * + */ +class MessageArtifactWorker extends SwingWorker { + + private BlackboardArtifact artifact; + private static final Logger logger = Logger.getLogger(MessageArtifactWorker.class.getName()); + + private static final BlackboardAttribute.Type TSK_ASSOCIATED_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); + + MessageArtifactWorker(BlackboardArtifact artifact) { + this.artifact = artifact; + } + + @Override + protected MesssageArtifactData doInBackground() throws Exception { + /* + * If the artifact is a keyword hit, use the associated artifact as the + * one to show in this viewer + */ + if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { + try { + getAssociatedArtifact(artifact).ifPresent(associatedArtifact -> { + this.artifact = associatedArtifact; + }); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "error getting associated artifact", ex); + } + } + + Map map = getAttributesForArtifact(artifact); + Set attachements = getAttachments(artifact); + + if (isCancelled()) { + return null; + } + + return new MesssageArtifactData(artifact, map, attachements); + } + + /** + * Returns a map containing all of the attributes for the given artifact. + * + * @param artifact Artifact to get the attributes for. + * + * @return A map of all the attributes available for the given artifact. + * + * @throws TskCoreException + */ + static private Map getAttributesForArtifact(BlackboardArtifact artifact) throws TskCoreException { + Map attributeMap = new HashMap<>(); + for (BlackboardAttribute attribute : artifact.getAttributes()) { + attributeMap.put(attribute.getAttributeType(), attribute); + } + + return attributeMap; + } + + /** + * Returns the set of for the given artifact. + * + * @param art Message artifact to get attachements from. + * + * @return A set of attachments objects, or empty list if none were found; + * + * @throws TskCoreException + */ + private Set getAttachments(BlackboardArtifact art) throws TskCoreException { + + final Set attachments = new HashSet<>(); + + // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute + BlackboardAttribute attachmentsAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if (attachmentsAttr != null) { + try { + MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class); + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (MessageAttachments.FileAttachment fileAttachment : fileAttachments) { + attachments.add(fileAttachment); + } + Collection urlAttachments = msgAttachments.getUrlAttachments(); + for (MessageAttachments.URLAttachment urlAttachment : urlAttachments) { + attachments.add(urlAttachment); + } + } catch (BlackboardJsonAttrUtil.InvalidJsonException ex) { + logger.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", art.getName()), ex); + } + } else { // For backward compatibility - email attachements are derived files and children of the email message artifact + for (Content child : art.getChildren()) { + if (child instanceof AbstractFile) { + attachments.add(new MessageAttachments.FileAttachment((AbstractFile) child)); + } + } + } + + return attachments; + } + + /** + * Get the artifact associated with the given artifact, if there is one. + * + * @param artifact The artifact to get the associated artifact from. Must + * not be null + * + * @throws TskCoreException If there is a critical error querying the DB. + * @return An optional containing the artifact associated with the given + * artifact, if there is one. + */ + static Optional getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException { + BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE); + if (attribute != null) { + return Optional.of(artifact.getSleuthkitCase().getArtifactByArtifactId(attribute.getValueLong())); + } + return Optional.empty(); + } + + /** + * Object to store the data gathered by the worker thread. + */ + static class MesssageArtifactData { + + private final BlackboardArtifact artifact; + private final Map attributeMap; + private final Set attachements; + + MesssageArtifactData(BlackboardArtifact artifact, Map attributeMap, Set attachements) { + this.artifact = artifact; + this.attributeMap = attributeMap; + this.attachements = attachements; + } + + BlackboardArtifact getArtifact() { + return artifact; + } + + Map getAttributeMap() { + return attributeMap; + } + + Set getAttachements() { + return attachements; + } + + /** + * Returns the display string for the given attribute. + * + * @param attributeType Desired attribute. + * + * @return Display string for attribute or empty string if the attribute + * was not found. + */ + String getAttributeDisplayString(BlackboardAttribute.ATTRIBUTE_TYPE attributeType) { + BlackboardAttribute attribute = attributeMap.get(new BlackboardAttribute.Type(attributeType)); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + } +} From 51ea90c33bb2212e02d8ab1a45bb5f9a05361d46 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 20 May 2021 20:27:44 -0400 Subject: [PATCH 26/78] fix --- .../objectdetection/ObjectDetectectionFileIngestModule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java index bff9d28be8..08a5be91d3 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java @@ -47,7 +47,6 @@ import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; From 0fca7439db5ddcf72e02805923d02445037ff850 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 May 2021 08:18:27 -0400 Subject: [PATCH 27/78] undo formatting for LEAPP file processor --- .../leappanalyzers/LeappFileProcessor.java | 207 +++++++++--------- 1 file changed, 100 insertions(+), 107 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java index f17f5469aa..06ee9efe4c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java @@ -108,10 +108,10 @@ public final class LeappFileProcessor { * Main constructor. * * @param attributeType The BlackboardAttribute type or null if not - * used. used. - * @param columnName The name of the column in the tsv file. - * @param required Whether or not this attribute is required to be - * present. + * used. used. + * @param columnName The name of the column in the tsv file. + * @param required Whether or not this attribute is required to be + * present. */ TsvColumn(BlackboardAttribute.Type attributeType, String columnName, boolean required) { this.attributeType = attributeType; @@ -275,7 +275,7 @@ public final class LeappFileProcessor { * Process the Leapp files that were found that match the xml mapping file * * @param LeappFilesToProcess List of files to process - * @param LeappImageFile Abstract file to create artifact for + * @param LeappImageFile Abstract file to create artifact for * * @throws FileNotFoundException * @throws IOException @@ -308,7 +308,7 @@ public final class LeappFileProcessor { * Process the Leapp files that were found that match the xml mapping file * * @param LeappFilesToProcess List of files to process - * @param dataSource The data source. + * @param dataSource The data source. * * @throws FileNotFoundException * @throws IOException @@ -318,7 +318,7 @@ public final class LeappFileProcessor { for (String LeappFileName : LeappFilesToProcess) { String fileName = FilenameUtils.getName(LeappFileName); - File LeappFile = new File(LeappFileName); + File LeappFile = new File(LeappFileName); if (tsvFileAttributes.containsKey(fileName)) { List attrList = tsvFileAttributes.get(fileName); BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName); @@ -345,7 +345,7 @@ public final class LeappFileProcessor { String trackpointSegmentName = null; GeoTrackPoints pointList = new GeoTrackPoints(); AbstractFile geoAbstractFile = null; - + if (LeappFile == null || !LeappFile.exists() || fileName == null) { logger.log(Level.WARNING, String.format("Leap file: %s is null or does not exist", LeappFile == null ? LeappFile.toString() : "")); return; @@ -405,11 +405,11 @@ public final class LeappFileProcessor { } } } - + try { if (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(), "norelationship").toLowerCase() == "trackpoint") { - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, "", geoAbstractFile)).addTrack(trackpointSegmentName, pointList, new ArrayList<>()); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, "", geoAbstractFile)).addTrack(trackpointSegmentName, pointList, new ArrayList<>()); + } } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS @@ -418,9 +418,10 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact.",}) + "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact.", + }) - private void createRoute(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { + private void createRoute (Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { Double startLatitude = Double.valueOf(0); Double startLongitude = Double.valueOf(0); @@ -434,7 +435,7 @@ public final class LeappFileProcessor { String sourceFile = null; AbstractFile absFile = null; String comment = ""; - + try { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { @@ -477,16 +478,18 @@ public final class LeappFileProcessor { GeoWaypoints waypointList = new GeoWaypoints(); waypointList.addPoint(new Waypoint(startLatitude, startLongitude, zeroValue, "")); waypointList.addPoint(new Waypoint(endLatitude, endLongitude, zeroValue, locationName)); - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addRoute(destinationName, dateTime, waypointList, new ArrayList<>()); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addRoute(destinationName, dateTime, waypointList, new ArrayList<>()); + } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_waypoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS } - + + } - + @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact.",}) + "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact.", + }) private AbstractFile createTrackpoint(Collection bbattributes, Content dataSource, String fileName, String trackpointSegmentName, GeoTrackPoints pointList) throws IngestModuleException { @@ -500,7 +503,7 @@ public final class LeappFileProcessor { String sourceFile = null; String comment = null; AbstractFile absFile = null; - + try { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { @@ -536,26 +539,28 @@ public final class LeappFileProcessor { absFile = (AbstractFile) dataSource; } if ((trackpointSegmentName == null) || (trackpointSegmentName == segmentName)) { - trackpointSegmentName = segmentName; - pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); + trackpointSegmentName = segmentName; + pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); } else { - (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addTrack(segmentName, pointList, new ArrayList<>()); - trackpointSegmentName = segmentName; - pointList = new GeoTrackPoints(); - pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); - + (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addTrack(segmentName, pointList, new ArrayList<>()); + trackpointSegmentName = segmentName; + pointList = new GeoTrackPoints(); + pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime)); + } } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_trackpoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS } - - return absFile; - + + return absFile; + } + @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.",}) - + "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.", + }) + private void createMessageRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String messageType = null; @@ -609,7 +614,7 @@ public final class LeappFileProcessor { sourceFile = bba.getValueString(); break; case "TSK_READ_STATUS": - if (bba.getValueInt() == 1) { + if (bba.getValueInt() == 1 ) { messageStatus = MessageReadStatus.READ; } else { messageStatus = MessageReadStatus.UNREAD; @@ -633,19 +638,19 @@ public final class LeappFileProcessor { AbstractFile absFile = findAbstractFile(dataSource, sourceFile); if (absFile == null) { absFile = (AbstractFile) dataSource; - } + } CommunicationArtifactsHelper accountArtifact; - Account.Type accountType = getAccountType(fileName); + Account.Type accountType = getAccountType(fileName); if (alternateId == null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact messageArtifact = accountArtifact.addMessage(messageType, communicationDirection, senderId, - receipentId, dateTime, messageStatus, subject, - messageText, threadId, otherAttributes); + receipentId, dateTime, messageStatus, subject, + messageText, threadId, otherAttributes); if (!fileAttachments.isEmpty()) { messageAttachments = new MessageAttachments(fileAttachments, new ArrayList<>()); accountArtifact.addAttachments(messageArtifact, messageAttachments); @@ -657,7 +662,8 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship.",}) + "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship.", + }) private void createContactRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String alternateId = null; @@ -709,14 +715,14 @@ public final class LeappFileProcessor { } Account.Type accountType = getAccountType(fileName); if (accountType != null) { - + CommunicationArtifactsHelper accountArtifact; if (alternateId == null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact messageArtifact = accountArtifact.addContact(contactName, phoneNumber, homePhoneNumber, mobilePhoneNumber, emailAddr, otherAttributes); } @@ -726,13 +732,14 @@ public final class LeappFileProcessor { } @NbBundle.Messages({ - "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship.",}) + "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship.", + }) private void createCalllogRelationship(Collection bbattributes, Content dataSource, String fileName) throws IngestModuleException { String callerId = null; String alternateId = null; - List calleeId = Arrays.asList(); + List calleeId = Arrays.asList(); CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN; Long startDateTime = Long.valueOf(0); Long endDateTime = Long.valueOf(0); @@ -744,14 +751,14 @@ public final class LeappFileProcessor { for (BlackboardAttribute bba : bbattributes) { switch (bba.getAttributeType().getTypeName()) { case "TSK_TEXT_FILE": - sourceFile = bba.getValueString(); - break; + sourceFile = bba.getValueString(); + break; case "TSK_DATETIME_START": - startDateTime = bba.getValueLong(); - break; + startDateTime = bba.getValueLong(); + break; case "TSK_DATETIME_END": - startDateTime = bba.getValueLong(); - break; + startDateTime = bba.getValueLong(); + break; case "TSK_DIRECTION": if (bba.getValueString().toLowerCase().equals("outgoing")) { communicationDirection = CommunicationDirection.OUTGOING; @@ -766,8 +773,8 @@ public final class LeappFileProcessor { break; case "TSK_PHONE_NUMBER_TO": if (!bba.getValueString().isEmpty()) { - String[] calleeTempList = bba.getValueString().split(",", 0); - calleeId = Arrays.asList(calleeTempList); + String [] calleeTempList = bba.getValueString().split(",", 0); + calleeId = Arrays.asList(calleeTempList); } break; case "TSK_ID": @@ -779,12 +786,12 @@ public final class LeappFileProcessor { break; } } - + if (calleeId.isEmpty() && communicationDirection == CommunicationDirection.OUTGOING) { - String[] calleeTempList = callerId.split(",", 0); - calleeId = Arrays.asList(calleeTempList); - callerId = null; - } + String [] calleeTempList = callerId.split(",", 0); + calleeId = Arrays.asList(calleeTempList); + callerId = null; + } AbstractFile absFile = findAbstractFile(dataSource, sourceFile); if (absFile == null) { absFile = (AbstractFile) dataSource; @@ -793,10 +800,10 @@ public final class LeappFileProcessor { CommunicationArtifactsHelper accountArtifact; if (accountType != null) { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType); + moduleName, absFile, accountType); } else { accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), - moduleName, absFile, accountType, accountType, alternateId); + moduleName, absFile, accountType, accountType, alternateId); } BlackboardArtifact callLogArtifact = accountArtifact.addCalllog(communicationDirection, callerId, calleeId, startDateTime, endDateTime, mediaType, otherAttributes); } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) { @@ -804,7 +811,7 @@ public final class LeappFileProcessor { } } - + private Account.Type getAccountType(String AccountTypeName) { switch (AccountTypeName.toLowerCase()) { case "zapya.tsv": @@ -842,7 +849,7 @@ public final class LeappFileProcessor { case "whatsapp - contacts.tsv": return Account.Type.WHATSAPP; case "tangomessages messages.tsv": - return Account.Type.TANGO; + return Account.Type.TANGO; case "shareit file transfer.tsv": return Account.Type.SHAREIT; case "line - calllogs.tsv": @@ -873,22 +880,20 @@ public final class LeappFileProcessor { return Account.Type.PHONE; } } - + /** * Process the line read and create the necessary attributes for it. * - * @param lineValues List of column values. + * @param lineValues List of column values. * @param columnIndexes Mapping of column headers (trimmed; to lower case) - * to column index. All header columns and only all - * header columns should be present. - * @param attrList The list of attributes as specified for the schema - * of this file. - * @param fileName The name of the file being processed. - * @param lineNum The line number in the file. - * + * to column index. All header columns and only all header columns should be + * present. + * @param attrList The list of attributes as specified for the schema of + * this file. + * @param fileName The name of the file being processed. + * @param lineNum The line number in the file. * @return The collection of blackboard attributes for the artifact created - * from this line. - * + * from this line. * @throws IngestModuleException */ private Collection processReadLine(List lineValues, Map columnIndexes, @@ -944,10 +949,9 @@ public final class LeappFileProcessor { * Check type of attribute and possibly format string based on it. * * @param colAttr Column Attribute information - * @param value string to be formatted - * + * @param value string to be formatted * @return formatted string based on attribute type if no attribute type - * found then return original string + * found then return original string */ private String formatValueBasedOnAttrType(TsvColumn colAttr, String value) { if (colAttr.getAttributeType().getTypeName().equals("TSK_DOMAIN")) { @@ -967,10 +971,9 @@ public final class LeappFileProcessor { * value. * * @param attrType The attribute type. - * @param value The string value to be converted to the appropriate data - * type for the attribute type. + * @param value The string value to be converted to the appropriate data + * type for the attribute type. * @param fileName The file name that the value comes from. - * * @return The generated blackboard attribute. */ private BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String value, String fileName) { @@ -1019,9 +1022,7 @@ public final class LeappFileProcessor { * Handles converting a string value to a blackboard attribute. * * @param orig The original string value. - * * @return The generated blackboard attribute. - * * @throws ParseException * @throws NumberFormatException */ @@ -1032,15 +1033,13 @@ public final class LeappFileProcessor { * Runs parsing function on string value to convert to right data type and * generates a blackboard attribute for that converted data type. * - * @param value The string value. - * @param attrType The blackboard attribute type. - * @param fileName The name of the file from which the value comes. - * @param blankIsNull If string is blank return null attribute. - * @param zeroIsNull If string is some version of 0, return null - * attribute. + * @param value The string value. + * @param attrType The blackboard attribute type. + * @param fileName The name of the file from which the value comes. + * @param blankIsNull If string is blank return null attribute. + * @param zeroIsNull If string is some version of 0, return null attribute. * @param valueConverter The means of converting the string value to an - * appropriate blackboard attribute. - * + * appropriate blackboard attribute. * @return The generated blackboard attribute or null if not determined. */ private BlackboardAttribute parseAttrValue(String value, BlackboardAttribute.Type attrType, String fileName, boolean blankIsNull, boolean zeroIsNull, ParseExceptionFunction valueConverter) { @@ -1158,7 +1157,7 @@ public final class LeappFileProcessor { for (int k = 0; k < attributeNlist.getLength(); k++) { NamedNodeMap nnm = attributeNlist.item(k).getAttributes(); String attributeName = nnm.getNamedItem("attributename").getNodeValue(); - + if (!attributeName.toLowerCase().matches("null")) { String columnName = nnm.getNamedItem("columnName").getNodeValue(); String required = nnm.getNamedItem("required").getNodeValue(); @@ -1208,19 +1207,13 @@ public final class LeappFileProcessor { } /** - * Generic method for creating a blackboard artifact with attributes - * - * NOTE: - * Handles Analysis results in a generic manner. If special handling for - * score, conclusion, configuration, justification is needed for an analysis - * result type, this will need to be updated. + * Generic method for creating a blackboard artifact with attributes * - * @param artType The artifact type. - * @param dataSource is the Content object that needs to have the artifact - * added for it + * @param artType The artifact type. + * @param dataSource is the Content object that needs to have the artifact + * added for it * @param bbattributes is the collection of blackboard attributes that need - * to be added to the artifact after the artifact has - * been created + * to be added to the artifact after the artifact has been created * * @return The newly-created artifact, or null on error */ @@ -1245,7 +1238,7 @@ public final class LeappFileProcessor { * Method to post a list of BlackboardArtifacts to the blackboard. * * @param artifacts A list of artifacts. IF list is empty or null, the - * function will return. + * function will return. */ void postArtifacts(Collection artifacts) { if (artifacts == null || artifacts.isEmpty()) { @@ -1266,7 +1259,7 @@ public final class LeappFileProcessor { */ private void configExtractor() throws IOException { PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class, - xmlFile, true); + xmlFile, true); } private static final Set ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz")); @@ -1323,14 +1316,14 @@ public final class LeappFileProcessor { } } - + private AbstractFile findAbstractFile(Content dataSource, String fileNamePath) { if (fileNamePath == null) { return null; } - + List files; - + String fileName = FilenameUtils.getName(fileNamePath); String filePath = FilenameUtils.normalize(FilenameUtils.getPath(fileNamePath), true); @@ -1354,4 +1347,4 @@ public final class LeappFileProcessor { return null; } -} + } From 761b1aeace940dd197cc751de6896a7f4df6ec9a Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 21 May 2021 16:47:19 -0400 Subject: [PATCH 28/78] Added swingworkers to selection handlers --- .../OtherOccurrencesFilesTableModel.java | 2 +- .../contentviewer/OtherOccurrencesPanel.java | 206 +++++++++++++----- 2 files changed, 147 insertions(+), 61 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java index c64acaaa4f..284255bfae 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesFilesTableModel.java @@ -110,7 +110,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel { * @param newNodeData data to add to the table */ void addNodeData(NodeData newNodeData) { - String newNodeKey = createNodeKey((NodeData) newNodeData);//FilenameUtils.getName(((OtherOccurrenceNodeInstanceData)newNodeData).getFilePath()); + String newNodeKey = createNodeKey(newNodeData); List nodeList = nodeMap.get(newNodeKey); if (nodeList == null) { nodeKeys.add(newNodeKey); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java index 97552d8091..c4d0bf0e0a 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrencesPanel.java @@ -46,6 +46,7 @@ import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -82,6 +83,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { private String deviceId = ""; //the device id of the data source for the file which the content viewer is being populated for private AbstractFile file = null; + private SwingWorker worker; + /** * Creates new form OtherOccurrencesPanel */ @@ -95,7 +98,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { customizeComponents(); } - private void customizeComponents() { ActionListener actList = (ActionEvent e) -> { JMenuItem jmi = (JMenuItem) e.getSource(); @@ -226,7 +228,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { details = eamCase.getCaseDetailsOptionsPaneDialog(); } else { details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetails(); - } + } } else { details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetailsReference(); } @@ -261,7 +263,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } } } - + @NbBundle.Messages({"OtherOccurrencesPanel_earliestCaseNotAvailable=Not Availble."}) /** * Reset the UI and clear cached data. @@ -291,12 +293,21 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * @param value The value being correlated on. */ public void populateTableForOneType(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException { + if (worker != null) { + worker.cancel(true); + worker = null; + } + casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); - OtherOccurrenceOneTypeWorker worker = new OtherOccurrenceOneTypeWorker(aType, value, file, deviceId, dataSourceName) { + worker = new OtherOccurrenceOneTypeWorker(aType, value, file, deviceId, dataSourceName) { @Override public void done() { try { + if (isCancelled()) { + return; + } + casesTableModel.clearTable(); OtherOccurrenceOneTypeWorker.OneTypeData data = get(); @@ -317,7 +328,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } } catch (InterruptedException | ExecutionException ex) { - logger.log(Level.SEVERE, "Failed to "); + logger.log(Level.SEVERE, "Failed to update OtherOccurrence panel", ex); } } }; @@ -372,29 +383,44 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * selection */ private void updateOnCaseSelection() { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - int[] selectedCaseIndexes = casesTable.getSelectedRows(); - dataSourcesTableModel.clearTable(); - filesTableModel.clearTable(); - if (selectedCaseIndexes.length == 0) { - //special case when no cases are selected - occurrencePanel = new OccurrencePanel(); - occurrencePanel.getPreferredSize(); - detailsPanelScrollPane.setViewportView(occurrencePanel); - } else { - String currentCaseName; - try { - currentCaseName = Case.getCurrentCaseThrows().getName(); - } catch (NoCurrentCaseException ex) { - currentCaseName = null; - logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex); - } - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + if (worker != null) { + worker.cancel(true); + worker = null; + } + + final int[] selectedCaseIndexes = casesTable.getSelectedRows(); + dataSourcesTableModel.clearTable(); + filesTableModel.clearTable(); + + if (selectedCaseIndexes.length == 0) { + //special case when no cases are selected + occurrencePanel = new OccurrencePanel(); + occurrencePanel.getPreferredSize(); + detailsPanelScrollPane.setViewportView(occurrencePanel); + + return; + } + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) { + @Override + public void done() { + if (isCancelled()) { + return; + } + + try { + Map correlatedNodeDataMap = get(); + + String currentCaseName; + try { + currentCaseName = Case.getCurrentCaseThrows().getName(); + } catch (NoCurrentCaseException ex) { + currentCaseName = null; + logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex); + } - // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); for (NodeData nodeData : correlatedNodeDataMap.values()) { for (int selectedRow : selectedCaseIndexes) { try { @@ -411,14 +437,19 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { } } } - } - if (dataSourcesTable.getRowCount() > 0) { - dataSourcesTable.setRowSelectionInterval(0, 0); + if (dataSourcesTable.getRowCount() > 0) { + dataSourcesTable.setRowSelectionInterval(0, 0); + } + + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel on data source selection", ex); } } - } finally { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } + }; + + worker.execute(); } /** @@ -426,40 +457,55 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { * selection */ private void updateOnDataSourceSelection() { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - int[] selectedDataSources = dataSourcesTable.getSelectedRows(); - filesTableModel.clearTable(); - for (CorrelationAttributeInstance corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + if (worker != null) { + worker.cancel(true); + worker = null; + } - // get correlation and reference set instances from DB - correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr)); - for (NodeData nodeData : correlatedNodeDataMap.values()) { - for (int selectedDataSourceRow : selectedDataSources) { - try { - if (nodeData.isCentralRepoNode()) { - if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID()) - && dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { - filesTableModel.addNodeData(nodeData); - } - } else { - if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { - filesTableModel.addNodeData(nodeData); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + final int[] selectedDataSources = dataSourcesTable.getSelectedRows(); + filesTableModel.clearTable(); + + worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) { + @Override + public void done() { + if (isCancelled()) { + return; + } + + try { + Map correlatedNodeDataMap = get(); + for (NodeData nodeData : correlatedNodeDataMap.values()) { + for (int selectedDataSourceRow : selectedDataSources) { + try { + if (nodeData.isCentralRepoNode()) { + if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID()) + && dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { + filesTableModel.addNodeData(nodeData); + } + } else { + if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { + filesTableModel.addNodeData(nodeData); + } } + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex); } } + if (filesTable.getRowCount() > 0) { + filesTable.setRowSelectionInterval(0, 0); + } + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel on case selection", ex); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } - if (filesTable.getRowCount() > 0) { - filesTable.setRowSelectionInterval(0, 0); - } - } finally { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } + }; + + worker.execute(); } /** @@ -536,6 +582,46 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel { return ""; } + /** + * SwingWorker used by the case and data source selection handler. + */ + private class SelectionWorker extends SwingWorker, Void> { + + private final Collection coAtInstances; + private final AbstractFile abstractFile; + private final String deviceIdStr; + private final String dataSourceNameStr; + + /** + * Construct a new SelectionWorker. + * + * @param coAtInstances + * @param abstractFile + * @param deviceIdStr + * @param dataSourceNameStr + */ + SelectionWorker(Collection coAtInstances, AbstractFile abstractFile, String deviceIdStr, String dataSourceNameStr) { + this.coAtInstances = coAtInstances; + this.abstractFile = abstractFile; + this.dataSourceNameStr = dataSourceNameStr; + this.deviceIdStr = deviceIdStr; + } + + @Override + protected Map doInBackground() throws Exception { + Map correlatedNodeDataMap = new HashMap<>(); + for (CorrelationAttributeInstance corAttr : coAtInstances) { + correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(abstractFile, deviceIdStr, dataSourceNameStr, corAttr)); + + if(isCancelled()) { + return new HashMap<>(); + } + } + + return correlatedNodeDataMap; + } + } + /** * SwingWorker for creating the CSV dump file. */ From 0fea6d4525cf13a053d9c910110cb6ffc090657f Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 24 May 2021 14:24:18 -0400 Subject: [PATCH 29/78] Update VcardParser.java Check if telephone uri is null and return if it is. --- .../sleuthkit/autopsy/thunderbirdparser/VcardParser.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java index fadaa720b1..fad1afdeb9 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java @@ -388,6 +388,9 @@ final class VcardParser { String telephoneText = telephone.getText(); if (telephoneText == null || telephoneText.isEmpty()) { + if (telephone.getUri() == null) { + return; + } telephoneText = telephone.getUri().getNumber(); if (telephoneText == null || telephoneText.isEmpty()) { return; @@ -502,6 +505,9 @@ final class VcardParser { private void addPhoneAccountInstances(Telephone telephone, AbstractFile abstractFile, Collection accountInstances) { String telephoneText = telephone.getText(); if (telephoneText == null || telephoneText.isEmpty()) { + if (telephone.getUri() == null) { + return; + } telephoneText = telephone.getUri().getNumber(); if (telephoneText == null || telephoneText.isEmpty()) { return; From 917acfd1d28501bc4891fe352e9ba8f20ffbcd31 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 24 May 2021 14:31:06 -0400 Subject: [PATCH 30/78] Removed the db calls for the sql viewer from the edt --- .../autopsy/contentviewers/SQLiteViewer.java | 169 ++++++++++++++---- 1 file changed, 131 insertions(+), 38 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index e9ba40eef0..e3b4c7ac7d 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -32,11 +32,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.logging.Level; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; @@ -71,6 +73,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed + SwingWorker worker; + /** * Constructs a file content viewer for SQLite database files. */ @@ -326,11 +330,17 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public void setFile(AbstractFile file) { - WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - sqliteDbFile = file; - initReader(); - processSQLiteFile(); - WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + if (worker != null) { + worker.cancel(true); + worker = null; + } + resetComponent(); + + if (file == null) { + return; + } + + processSQLiteFile(file); } @Override @@ -367,25 +377,40 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) - private void processSQLiteFile() { - try { - tablesDropdownList.removeAllItems(); + private void processSQLiteFile(final AbstractFile file) { - Collection dbTablesMap = viewReader.getTableNames(); - if (dbTablesMap.isEmpty()) { - tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); - tablesDropdownList.setEnabled(false); - } else { - dbTablesMap.forEach((tableName) -> { - tablesDropdownList.addItem(tableName); - }); + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + worker = new SQLiteViewerWorker(file) { + @Override + public void done() { + if (isCancelled()) { + return; + } + + WorkerResults results; + try { + results = get(); + sqliteDbFile = file; + viewReader = results.getReader(); + tablesDropdownList.removeAllItems(); + Collection dbTablesMap = results.getDbTablesMap(); + if (dbTablesMap.isEmpty()) { + tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); + tablesDropdownList.setEnabled(false); + } else { + dbTablesMap.forEach((tableName) -> { + tablesDropdownList.addItem(tableName); + }); + } + + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Failed to display SQL Viewer for file (%d)", file.getId()), ex); + } } - } catch (SQLiteTableReaderException ex) { - logger.log(Level.WARNING, String.format("Unable to get table names " - + "from sqlite file [%s] with id=[%d].", sqliteDbFile.getName(), - sqliteDbFile.getId(), ex.getMessage())); - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); - } + }; + + worker.execute(); } @NbBundle.Messages({"# {0} - tableName", @@ -455,8 +480,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * data type. For our use, we want to define an action when encountering * column names and an action for all other data types. */ - private void initReader() { - viewReader = new SQLiteTableReader.Builder(sqliteDbFile) + private SQLiteTableReader initReader(AbstractFile sqliteFile) { + return new SQLiteTableReader.Builder(sqliteFile) .forAllColumnNames((columnName) -> { currentTableHeader.add(columnName); }) @@ -506,21 +531,44 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.exportTableToCsv.TableName=Table name: " }) private void exportTableToCsv(File file) { - File csvFile = new File(file.toString() + ".csv"); - String tableName = (String) this.tablesDropdownList.getSelectedItem(); - try (FileOutputStream out = new FileOutputStream(csvFile, false)) { - try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile) - .forAllColumnNames(getColumnNameCSVStrategy(out)) - .forAllTableValues(getForAllCSVStrategy(out)).build()) { - totalColumnCount = sqliteStream.getColumnCount(tableName); - sqliteStream.read(tableName); + final File csvFile = new File(file.toString() + ".csv"); + final String tableName = (String) this.tablesDropdownList.getSelectedItem(); + + SwingWorker csvWorker = new SwingWorker() { + @Override + protected String doInBackground() throws Exception { + try (FileOutputStream out = new FileOutputStream(csvFile, false)) { + try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile) + .forAllColumnNames(getColumnNameCSVStrategy(out)) + .forAllTableValues(getForAllCSVStrategy(out)).build()) { + totalColumnCount = sqliteStream.getColumnCount(tableName); + sqliteStream.read(tableName); + } + } catch (IOException | SQLiteTableReaderException | RuntimeException ex) { + logger.log(Level.WARNING, String.format("Failed to export table [%s]" + + " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), + sqliteDbFile.getId()), ex.getMessage()); //NON-NLS + + return Bundle.SQLiteViewer_exportTableToCsv_write_errText(); + } + return ""; } - } catch (IOException | SQLiteTableReaderException | RuntimeException ex) { - logger.log(Level.WARNING, String.format("Failed to export table [%s]" - + " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), - sqliteDbFile.getId()), ex.getMessage()); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); - } + + @Override + public void done() { + try { + String message = get(); + if (!message.isEmpty()) { + MessageNotifyUtil.Message.error(message); + } + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Failure occurred writing sql csv file.", ex); + } + } + + }; + + csvWorker.execute(); } /** @@ -622,4 +670,49 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public boolean isSupported(AbstractFile file) { return true; } + + /** + * SwingWorker that will gather the data needed to display the given + * file in the SQL viewer. + */ + private class SQLiteViewerWorker extends SwingWorker { + + private final AbstractFile file; + + SQLiteViewerWorker(AbstractFile file) { + this.file = file; + } + + @Override + protected WorkerResults doInBackground() throws Exception { + SQLiteTableReader reader = initReader(file); + Collection dbTablesMap = reader.getTableNames(); + + return new WorkerResults(reader, dbTablesMap); + } + + } + + /* + * Stores the data gather from the + */ + private class WorkerResults { + + private final SQLiteTableReader reader; + private final Collection dbTablesMap; + + WorkerResults(SQLiteTableReader reader, Collection dbTablesMap) { + this.reader = reader; + this.dbTablesMap = dbTablesMap; + } + + SQLiteTableReader getReader() { + return reader; + } + + Collection getDbTablesMap() { + return dbTablesMap; + } + } + } From f1c25b4fef34234531e54f46df99c23c09cce748 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 24 May 2021 17:30:09 -0400 Subject: [PATCH 31/78] Initial pass of intializing hasData\hasDataSource --- .../sleuthkit/autopsy/casemodule/Case.java | 116 +++++++++++++++--- 1 file changed, 102 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index d2233a93cb..8e2e263cd9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -26,6 +26,7 @@ import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; @@ -190,6 +191,9 @@ public class Case { private final SleuthkitEventListener sleuthkitEventListener; private CollaborationMonitor collaborationMonitor; private Services caseServices; + + private volatile boolean hasDataSource = false; + private volatile boolean hasData = false; /* * Get a reference to the main window of the desktop application to use to @@ -1032,6 +1036,7 @@ public class Case { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase(); openCaseAction = newCurrentCase::open; } + newCurrentCase.initializeDataListners(); newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); currentCase = newCurrentCase; logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS @@ -1672,20 +1677,11 @@ public class Case { * @return True or false. */ public boolean hasData() { - boolean hasDataSources = false; - String query = "SELECT count(*) AS count FROM data_source_info"; - try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { - ResultSet resultSet = dbQuery.getResultSet(); - if (resultSet.next()) { - long numDataSources = resultSet.getLong("count"); - if (numDataSources > 0) { - hasDataSources = true; - } - } - } catch (TskCoreException | SQLException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - } - return hasDataSources; + return hasData; + } + + public boolean hasDataSources() { + return hasDataSource; } /** @@ -2731,8 +2727,10 @@ public class Case { String databaseName = metadata.getCaseDatabaseName(); if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString()); + initializeDataParameters(); } else if (UserPreferences.getIsMultiUserModeEnabled()) { caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory()); + initializeDataParameters(); } else { throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled()); } @@ -3512,6 +3510,96 @@ public class Case { } } + + private long getDataSourceCount() throws TskCoreException { + long numDataSources = 0; + String query = "SELECT count(*) AS count FROM data_source_info"; + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + if (resultSet.next()) { + numDataSources = resultSet.getLong("count"); + } + } catch ( SQLException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS + throw new TskCoreException("Error accessing case databse", ex); + } + + return numDataSources; + } + + /** + * Initialize the hasData and hasDataSource parameters by checking the + * database. + * + * hasDataSource will be true if any data Source exists the db. + * + * hasData will be true if hasDataSource is true or if there are entries + * in the tsk_object or tsk_host tables. + * + * @throws TskCoreException + */ + private void initializeDataParameters() throws TskCoreException { + hasDataSource = getDataSourceCount() > 0; + + if(!hasDataSource) { + // Check to see if there are objects like os accounts or reports + // which both have object ids assocated with them. + String query = "SELECT count(*) AS count FROM tsk_objects"; + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + if (resultSet.next()) { + hasData = resultSet.getLong("count") > 0; + } + } catch ( SQLException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS + throw new TskCoreException("Error accessing case databse", ex); + } + } else { + hasData = true; + } + + if(!hasData) { + // No object, but check to see if there are any hosts. + String query = "SELECT count(*) AS count FROM tsk_hosts"; + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + if (resultSet.next()) { + hasData = resultSet.getLong("count") > 0; + } + } catch ( SQLException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS + throw new TskCoreException("Error accessing case databse", ex); + } + } + } + + /** + * Create the listeners, if needed, to update hasDataSource and hasData. + */ + private void initializeDataListners() { + if(!hasDataSource) { + addEventTypeSubscriber(Collections.singleton(Case.Events.DATA_SOURCE_ADDED), new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + hasDataSource = true; + hasData = true; + } + }); + } + + if(!hasData) { + PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + hasData = true; + } + }; + + addEventTypeSubscriber(Collections.singleton(Case.Events.HOSTS_ADDED),listener); + addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED),listener); + addEventTypeSubscriber(Collections.singleton(Case.Events.REPORT_ADDED),listener); + } + } /** * Defines the signature for case action methods that can be passed as From 217c6c86f30ec0845432de932e59a224892d7695 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 24 May 2021 17:57:12 -0400 Subject: [PATCH 32/78] Added swing worker to MessageDataContent setNode --- .../relationships/MessageDataContent.java | 46 +++++++++++++++++-- .../MessageArtifactViewer.java | 2 +- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java index cf4101a059..4c6018e5dc 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java @@ -20,9 +20,12 @@ package org.sleuthkit.autopsy.communications.relationships; import java.beans.PropertyChangeEvent; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.swing.SwingWorker; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactViewer; @@ -50,6 +53,8 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont private static final long serialVersionUID = 1L; final private ExplorerManager explorerManager = new ExplorerManager(); + + private ArtifactFetcher worker; @Override public void propertyChange(final PropertyChangeEvent evt) { @@ -63,11 +68,18 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont @Override public void setNode(Node node) { - BlackboardArtifact artifact = null; - if (node != null) { - artifact = getNodeArtifact(node); + if(worker != null) { + worker.cancel(true); + worker = null; } - setArtifact(artifact); + + if(node == null) { + resetComponent(); + return; + } + + worker = new ArtifactFetcher(node); + worker.execute(); } /** @@ -109,4 +121,30 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont return nodeArtifact; } + + private class ArtifactFetcher extends SwingWorker { + private final Node node; + + ArtifactFetcher(Node node) { + this.node = node; + } + + @Override + protected BlackboardArtifact doInBackground() throws Exception { + return getNodeArtifact(node); + } + + @Override + public void done() { + if(isCancelled()) { + return; + } + + try { + setArtifact(get()); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Failed to get node for artifact.", ex); //NON-NLS + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java index d72f6052b8..963630c591 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageArtifactViewer.java @@ -461,7 +461,7 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac return this; } - private void resetComponent() { + public void resetComponent() { // reset all fields fromText.setText(""); fromLabel.setEnabled(false); From b4aa5e9c52143fffd02a47ba76ff3177c78de611 Mon Sep 17 00:00:00 2001 From: apriestman Date: Tue, 25 May 2021 08:58:21 -0400 Subject: [PATCH 33/78] Remove name from extracted files --- .../modules/embeddedfileextractor/SevenZipExtractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index ec4d803fa1..6fdb53a5c3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -724,7 +724,7 @@ class SevenZipExtractor { if (checkForIngestCancellation(archiveFile)) { return false; } - final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName()); + final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex); final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName; final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName; From 04d295f65a19c1919cc39811f8ae224c09c140fd Mon Sep 17 00:00:00 2001 From: apriestman Date: Tue, 25 May 2021 10:43:35 -0400 Subject: [PATCH 34/78] Use allocated versions of cache files when available --- .../recentactivity/ChromeCacheExtractor.java | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index 5dfb81431b..9435161a23 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -33,6 +33,8 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -565,8 +567,13 @@ final class ChromeCacheExtractor { List effFiles = fileManager.findFiles(dataSource, "f_%", cachePath); //NON-NLS for (AbstractFile abstractFile : effFiles ) { + String cacheKey = cachePath + abstractFile.getName(); if (cachePath.equals(abstractFile.getParentPath()) && abstractFile.isFile()) { - this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile); + // Don't overwrite an allocated version with an unallocated version + if (abstractFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) + || !externalFilesTable.containsKey(cacheKey)) { + this.externalFilesTable.put(cacheKey, abstractFile); + } } } } @@ -590,20 +597,49 @@ final class ChromeCacheExtractor { return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile()); } - List cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cacheFolderName); //NON-NLS if (!cacheFiles.isEmpty()) { - for (AbstractFile abstractFile: cacheFiles ) { - if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) { - return Optional.of(abstractFile); + // Sort the list for consistency. Preference is: + // - In correct subfolder and allocated + // - In correct subfolder and unallocated + // - In incorrect subfolder and allocated + Collections.sort(cacheFiles, new Comparator() { + @Override + public int compare(AbstractFile file1, AbstractFile file2) { + try { + if (file1.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR) + && ! file2.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) { + return -1; + } + + if (file2.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR) + && ! file1.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) { + return 1; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting unique path for file with ID " + file1.getId() + " or " + file2.getId(), ex); + } + + if (file1.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) + && ! file2.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { + return -1; + } + if (file2.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) + && ! file1.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { + return 1; + } + + return Long.compare(file1.getId(), file2.getId()); } - } + }); + + // The best match will be the first element return Optional.of(cacheFiles.get(0)); } return Optional.empty(); } - + /** * Finds the "index" file that exists in each user's cache. This is used to * enumerate all of the caches on the system. From 63b1c103bfea612a78e125fb2fd505ccee0d03ed Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 11:36:51 -0400 Subject: [PATCH 35/78] initial work --- .../AnalysisResultsContentViewer.form | 28 ++ .../AnalysisResultsContentViewer.java | 285 ++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form new file mode 100644 index 0000000000..5f3eab1a5f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form @@ -0,0 +1,28 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java new file mode 100644 index 0000000000..7416ef6d69 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -0,0 +1,285 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.contentviewers.analysisresults; + +import java.awt.Component; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Displays a list of analysis results as a content viewer. + */ +public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { + + private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); + private static final int PREFERRED_VALUE = 6; + + private static Optional getAggregateScore(Collection analysisResults) { + return analysisResults.stream() + .map(AnalysisResult::getScore) + .max(Comparator.naturalOrder()); + } + + private static String normalizeAttr(String originalAttrStr) { + return (originalAttrStr == null) ? "" : originalAttrStr.trim(); + } + + + private static class ResultDisplayAttributes { + + private final AnalysisResult analysisResult; + private final List> attributesToDisplay; + + ResultDisplayAttributes(AnalysisResult analysisResult, List> attributesToDisplay) { + this.analysisResult = analysisResult; + this.attributesToDisplay = attributesToDisplay; + } + + List> getAttributesToDisplay() { + return attributesToDisplay; + } + + AnalysisResult getAnalysisResult() { + return analysisResult; + } + } + + @Messages({ + "AnalysisResultsContentViewer_displayAttributes_score=Score", + "AnalysisResultsContentViewer_displayAttributes_type=Type", + "AnalysisResultsContentViewer_displayAttributes_configuration=Configuration", + "AnalysisResultsContentViewer_displayAttributes_conclusion=Conclusion" + }) + private static ResultDisplayAttributes getDisplayAttributes(AnalysisResult analysisResult) { + + String type = ""; + try { + type = normalizeAttr(analysisResult.getType().getDisplayName()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get type for analysis result with id: " + analysisResult.getArtifactID(), ex); + } + + Stream> baseAnalysisAttrs = Stream.of( + Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_score(), + normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), + Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_type(), + normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), + Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_configuration(), + normalizeAttr(analysisResult.getConfiguration())), + Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_conclusion(), + normalizeAttr(analysisResult.getConclusion())) + ); + + Stream> blackboardAttributes = Stream.empty(); + try { + + blackboardAttributes = analysisResult.getAttributes().stream() + .filter(attr -> attr != null && attr.getAttributeType() != null && attr.getAttributeType().getDisplayName() != null) + .map(attr -> Pair.of(attr.getAttributeType().getDisplayName(), normalizeAttr(attr.getDisplayString()))) + .sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey())); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get attributes for analysis result with id: " + analysisResult.getArtifactID(), ex); + } + + List> allDisplayAttributes = Stream.concat(baseAnalysisAttrs, blackboardAttributes) + .collect(Collectors.toList()); + + return new ResultDisplayAttributes(analysisResult, allDisplayAttributes); + } + + private static List getScoreOrderedResults(Collection analysisResults) { + return analysisResults.stream() + .filter(ar -> ar != null && ar.getScore() != null) + // reverse order to push more important scores to the top + .sorted((a, b) -> -a.getScore().compareTo(b.getScore())) + .collect(Collectors.toList()); + } + + private static List getDisplayAttributes(Collection analysisResults) { + return analysisResults.stream() + .map(AnalysisResultsContentViewer::getDisplayAttributes) + .collect(Collectors.toList()); + } + + private static class NodeAnalysisResults { + + private final Collection analysisResults; + private final Optional selectedResult; + + NodeAnalysisResults(Collection analysisResults, Optional selectedResult) { + this.analysisResults = analysisResults; + this.selectedResult = selectedResult; + } + + Collection getAnalysisResults() { + return analysisResults; + } + + Optional getSelectedResult() { + return selectedResult; + } + } + + private static NodeAnalysisResults getAnalysisResults(Node node) { + if (node == null) { + return new NodeAnalysisResults(Collections.emptyList(), Optional.empty()); + } + + Map allAnalysisResults = new HashMap<>(); + Optional selectedResult = Optional.empty(); + + AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); + if (abstractFile != null) { + try { + abstractFile.getAllAnalysisResults().stream() + .filter(ar -> ar != null) + .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); + } + } + + Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); + if (analysisResults.size() > 0) { + + List filteredResults = analysisResults.stream() + .filter(ar -> ar != null && ar.getScore() != null) + .collect(Collectors.toList()); + + filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + + selectedResult = filteredResults.stream() + .max((a,b) -> a.getScore().compareTo(b.getScore())); + } + + return new NodeAnalysisResults(allAnalysisResults.values(), selectedResult); + } + + private static void render(Node node) { + NodeAnalysisResults nodeResults = getAnalysisResults(node); + List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); + List displayAttributes = getDisplayAttributes(orderedAnalysisResults); + + // GVDTODO + + Optional selectedResult = nodeResults.getSelectedResult(); + if (selectedResult.isPresent()) { + // GVDTODO + } + } + + private Node selectedNode; + + + /** + * Creates new form AnalysisResultsContentViewer + */ + public AnalysisResultsContentViewer() { + initComponents(); + } + + @NbBundle.Messages({ + "AnalysisResultsContentViewer_title=Analysis Results" + }) + @Override + public String getTitle() { + return Bundle.AnalysisResultsContentViewer_title(); + } + + @NbBundle.Messages({ + "AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node." + }) + @Override + public String getToolTip() { + return Bundle.AnalysisResultsContentViewer_tooltip(); + } + + @Override + public DataContentViewer createInstance() { + return new AnalysisResultsContentViewer(); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void setNode(Node selectedNode) { + this.selectedNode = selectedNode; + } + + @Override + public boolean isSupported(Node node) { + return getAnalysisResults(node).getAnalysisResults().size() > 0; + } + + @Override + public int isPreferred(Node node) { + return PREFERRED_VALUE; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} From 485830340cd5f46a35b2e940b974150813b68e36 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 25 May 2021 12:04:33 -0400 Subject: [PATCH 36/78] Removed unessisary code from DataContentPanel that was causing EDT calls --- .../corecomponents/DataContentPanel.java | 42 ++++--------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index eef00e31a3..ae49ab720a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -134,43 +134,20 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, @Override public void setNode(Node selectedNode) { - // change the cursor to "waiting cursor" for this operation - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - + + if (workerThread != null) { + workerThread.cancel(true); + workerThread = null; + } + // Reset everything for (int index = 0; index < jTabbedPane1.getTabCount(); index++) { jTabbedPane1.setEnabledAt(index, false); viewers.get(index).resetComponent(); } - String defaultName = NbBundle.getMessage(DataContentTopComponent.class, "CTL_DataContentTopComponent"); - // set the file path - if (selectedNode == null) { - setName(defaultName); - } else { - Content content = selectedNode.getLookup().lookup(Content.class); - if (content != null) { - //String path = DataConversion.getformattedPath(ContentUtils.getDisplayPath(selectedNode.getLookup().lookup(Content.class)), 0); - String path = defaultName; - try { - path = content.getUniquePath(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for {0}", content); //NON-NLS - } - setName(path); - } else { - setName(defaultName); - } - } - - currentNode = selectedNode; - - if (workerThread != null) { - workerThread.cancel(true); - } - if (selectedNode != null) { - workerThread = new DataContentPanelWorker(currentNode); + workerThread = new DataContentPanelWorker(selectedNode); workerThread.execute(); } } @@ -287,9 +264,6 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, @Override protected WorkerResults doInBackground() throws Exception { - if (node == null) { - return null; - } List supportedViewers = new ArrayList<>(); int preferredViewerIndex = 0; @@ -312,7 +286,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, } } - + return new WorkerResults(node, supportedViewers, preferredViewerIndex); } From 8ae09432f726e09bcb7cfec72546579c1fcf028d Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 12:07:22 -0400 Subject: [PATCH 37/78] default method getting title for Node on DataContentViewer --- .../autopsy/contentviewers/Metadata.java | 26 ++++++++++++++++++- .../DataContentViewer.java | 10 +++++++ .../corecomponents/DataContentPanel.java | 10 +++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 2c695dfcb4..e17c2c6317 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -20,13 +20,13 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.Component; import java.awt.Cursor; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; @@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.DataSource; @@ -229,8 +230,31 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { @Override public String getTitle() { + return getTitle(null); + } + + @Messages({ + "Metadata_dataArtifactTitle=Source File Metadata" + }) + @Override + public String getTitle(Node node) { + if (node != null) { + Collection artifacts = node.getLookup().lookupAll(BlackboardArtifact.class); + for (BlackboardArtifact art : artifacts) { + try { + if (art != null && art.getType().getCategory() == Category.DATA_ARTIFACT) { + return Bundle.Metadata_dataArtifactTitle(); + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Unable to get artifact type for artifact with id: " + art.getArtifactID(), ex); + } + } + } + return NbBundle.getMessage(this.getClass(), "Metadata.title"); } + + @Override public String getToolTip() { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataContentViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataContentViewer.java index ee84e67293..561d343e72 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataContentViewer.java @@ -51,6 +51,16 @@ public interface DataContentViewer { * */ public String getTitle(); + + /** + * Returns the title of this viewer to display in the tab. + * + * @param node The node to be viewed in the DataContentViewer. + * @return the title of DataContentViewer. + */ + public default String getTitle(Node node) { + return getTitle(); + } /** * Returns a short description of this viewer to use as a tool tip for its diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index eef00e31a3..9975237c0d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -140,6 +140,12 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, // Reset everything for (int index = 0; index < jTabbedPane1.getTabCount(); index++) { jTabbedPane1.setEnabledAt(index, false); + String tabTitle = viewers.get(index).getTitle(selectedNode); + tabTitle = tabTitle == null ? "" : tabTitle; + if (!tabTitle.equals(jTabbedPane1.getTitleAt(index))) { + jTabbedPane1.setTitleAt(index, tabTitle); + } + viewers.get(index).resetComponent(); } @@ -266,6 +272,10 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, int isPreferred(Node node) { return this.wrapped.isPreferred(node); } + + String getTitle(Node node) { + return this.wrapped.getTitle(node); + } } /** From e17cc9cfe94ee883dd998c1dde3a92b6c9d3a77c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 12:15:39 -0400 Subject: [PATCH 38/78] bundle addition --- .../sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 4714416d46..dacd037aaa 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -105,6 +105,7 @@ Metadata.nodeText.exceptionNotice.text=Error getting file metadata: MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file: JPEGViewerDummy.jTextField1.text=jTextField1 +Metadata_dataArtifactTitle=Source File Metadata PDFViewer.encryptedDialog=This document is password protected. PDFViewer.errorDialog=An error occurred while opening this PDF document. Check the logs for more information. You may continue to use this feature on other PDF documents. PListNode.KeyCol=Key From 2fc346b29060d3f945222e19abc68741d2ed78db Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 12:38:29 -0400 Subject: [PATCH 39/78] remove certain actions for data artifacts --- .../autopsy/datamodel/BlackboardArtifactNode.java | 7 ++++++- .../autopsy/directorytree/DataResultFilterNode.java | 9 +++++++-- .../hashdatabase/HashDbContextMenuActionsProvider.java | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 5bfca75425..2278f6d5c8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -45,6 +45,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -81,6 +82,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; +import org.sleuthkit.datamodel.DataArtifact; /** * A BlackboardArtifactNode is an AbstractNode implementation that can be used @@ -438,7 +440,10 @@ public class BlackboardArtifactNode extends AbstractContentNode getActions() { ArrayList actions = new ArrayList<>(); Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); - if (!selectedFiles.isEmpty()) { + Collection dataArtifacts = Utilities.actionsGlobalContext().lookupAll(DataArtifact.class); + // don't show AddContentToHashDbAction for data artifacts but do if related abstract file + if (!selectedFiles.isEmpty() && dataArtifacts.isEmpty()) { actions.add(AddContentToHashDbAction.getInstance()); } return actions; From 5c097c14e16610d9fd7458347d8e8dba0d0dfd08 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 25 May 2021 14:07:41 -0400 Subject: [PATCH 40/78] Update VMExtractorIngestModule.java Check if valid VHD files are in the list of files to extract. --- .../vmextractor/VMExtractorIngestModule.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java index 56e6b3fbdb..f07042c999 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java @@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestModule; import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataSource; @@ -118,6 +119,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { try { // look for all VM files vmFiles = findVirtualMachineFiles(dataSource); + vmFiles = removeNonVHDFiles(vmFiles); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error querying case database", ex); //NON-NLS return ProcessResult.ERROR; @@ -237,6 +239,34 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { } return vmFiles; } + + /** + * Check all the files and if a file is a vhd then check to make sure it is a valid vhd using the mimetype + * @param vmFiles List of virtual machine abstract files to look at + * @return List of abstract files of virtual machine files. + */ + private static List removeNonVHDFiles(List vmFiles) { + List vFile = new ArrayList<>(); + + for (AbstractFile vmFile : vmFiles) { + if (vmFile.getNameExtension().equalsIgnoreCase("vhd")) { + FileTypeDetector fileTypeDetector = null; + try { + fileTypeDetector = new FileTypeDetector(); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + logger.log(Level.WARNING, String.format("Unable to create file type detector for determining MIME type for file %s with id of %d", vmFile.getName(), vmFile.getId())); + } + String mimeType = fileTypeDetector.getMIMEType(vmFile); + if (mimeType.equalsIgnoreCase("application/x-vhd")) { + vFile.add(vmFile); + } + } else { + vFile.add(vmFile); + } + } + + return vFile; + } /** * Writes out an abstract file to a specified output folder. From bd5561f1d29708d54c13ce51373bc5f537f829a9 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 15:41:30 -0400 Subject: [PATCH 41/78] initial work --- .../autopsy/corecomponents/Bundle.properties | 2 +- .../corecomponents/Bundle.properties-MERGED | 2 +- .../DataContentViewerArtifact.form | 17 +++++++---------- .../DataContentViewerArtifact.java | 16 ++++++++-------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 725d5d83d1..77789fddb8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -46,7 +46,7 @@ DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... DataContentViewerArtifact.errorText=Error retrieving result -DataContentViewerArtifact.title=Results +DataContentViewerArtifact.title=Data Artifacts DataContentViewerArtifact.toolTip=Displays Results associated with the file DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.err=Invalid page number diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index b39205f8e1..9fc731013d 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -111,7 +111,7 @@ DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... DataContentViewerArtifact.errorText=Error retrieving result -DataContentViewerArtifact.title=Results +DataContentViewerArtifact.title=Data Artifacts DataContentViewerArtifact.toolTip=Displays Results associated with the file DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.err=Invalid page number diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form index c4928f5111..8b555abbc0 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form @@ -21,7 +21,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -59,10 +59,10 @@ - + - + @@ -89,18 +89,15 @@ - - - - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index f27ac62a1a..e1ce4c264a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -39,6 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import java.util.Collections; import java.util.HashSet; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer; @@ -115,8 +116,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat jPanel1.setLayout(new java.awt.GridBagLayout()); totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N - totalPageLabel.setMaximumSize(new java.awt.Dimension(40, 16)); - totalPageLabel.setPreferredSize(new java.awt.Dimension(25, 16)); + totalPageLabel.setMaximumSize(null); + totalPageLabel.setPreferredSize(null); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 0; @@ -135,15 +136,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat jPanel1.add(ofLabel, gridBagConstraints); currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N - currentPageLabel.setMaximumSize(new java.awt.Dimension(38, 14)); - currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14)); - currentPageLabel.setPreferredSize(new java.awt.Dimension(20, 14)); + currentPageLabel.setMaximumSize(null); + currentPageLabel.setPreferredSize(null); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(4, 7, 0, 0); + gridBagConstraints.insets = new java.awt.Insets(3, 7, 0, 0); jPanel1.add(currentPageLabel, gridBagConstraints); pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N @@ -227,7 +227,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 561, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 738, Short.MAX_VALUE) .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( @@ -235,7 +235,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat .addGroup(layout.createSequentialGroup() .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 397, Short.MAX_VALUE)) + .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)) ); }// //GEN-END:initComponents From c11f8ae66d109768f84409cddc78c1c378b9163a Mon Sep 17 00:00:00 2001 From: apriestman Date: Tue, 25 May 2021 15:53:12 -0400 Subject: [PATCH 42/78] Use object ID for archive folder name --- .../EmbeddedFileExtractorIngestModule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 706570c23a..21b5fc5ee7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -217,15 +217,15 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda } /** - * Creates a unique name for a file by concatentating the file name and the - * file object id. + * Creates a unique name for a file. + * Currently this is just the file object id to prevent long paths and illegal characters. * * @param file The file. * * @return The unique file name. */ static String getUniqueName(AbstractFile file) { - return file.getName() + "_" + file.getId(); + return Long.toString(file.getId()); } } From 0ec59a3a7ee3762f1824e4b78ea099c48161591c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 15:57:21 -0400 Subject: [PATCH 43/78] updated justification --- .../fileextmismatch/FileExtMismatchIngestModule.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java index 52a713eade..c9b25cf8d6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java @@ -24,6 +24,7 @@ import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; +import org.python.icu.text.MessageFormat; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -142,9 +143,13 @@ public class FileExtMismatchIngestModule implements FileIngestModule { addToTotals(jobId, System.currentTimeMillis() - startTime); if (mismatchDetected) { + String justification = MessageFormat.format("File has an extension of {0} but mime type is {1}", + abstractFile.getNameExtension(), detector.getMIMEType(abstractFile)); + // add artifact BlackboardArtifact bart = abstractFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_EXT_MISMATCH_DETECTED, LIKELY_NOTABLE_SCORE, null, null, null, Collections.emptyList()) + BlackboardArtifact.Type.TSK_EXT_MISMATCH_DETECTED, LIKELY_NOTABLE_SCORE, + null, null, justification, Collections.emptyList()) .getAnalysisResult(); try { From 0042c034394afd7a7bd1483826e1c9f93e43de3e Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 16:05:15 -0400 Subject: [PATCH 44/78] message fix --- .../modules/fileextmismatch/FileExtMismatchIngestModule.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java index c9b25cf8d6..214b4ce3a6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java @@ -18,13 +18,13 @@ */ package org.sleuthkit.autopsy.modules.fileextmismatch; +import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; -import org.python.icu.text.MessageFormat; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -143,8 +143,7 @@ public class FileExtMismatchIngestModule implements FileIngestModule { addToTotals(jobId, System.currentTimeMillis() - startTime); if (mismatchDetected) { - String justification = MessageFormat.format("File has an extension of {0} but mime type is {1}", - abstractFile.getNameExtension(), detector.getMIMEType(abstractFile)); + String justification = MessageFormat.format("File has MIME type of {0}", detector.getMIMEType(abstractFile)); // add artifact BlackboardArtifact bart = abstractFile.newAnalysisResult( From 204c3390d5bd70e2c35640ff7a6594942e8fa49a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 May 2021 16:29:40 -0400 Subject: [PATCH 45/78] updates for analysis results --- .../datamodel/AbstractContentNode.java | 7 +++++- .../autopsy/datamodel/Artifacts.java | 25 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index bf9a7669c1..7e0ee1a883 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; @@ -348,7 +349,11 @@ public abstract class AbstractContentNode extends ContentNode protected Pair getScorePropertyAndDescription(List tags) { Score score = Score.SCORE_UNKNOWN; try { - score = this.content.getAggregateScore(); + if (content instanceof AnalysisResult) { + score = ((AnalysisResult) content).getScore(); + } else { + score = this.content.getAggregateScore(); + } } catch (TskCoreException ex) { logger.log(Level.WARNING, "Unable to get aggregate score for content with id: " + this.content.getId(), ex); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java index d39bef3ee9..976cc7dc3d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java @@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.guiutils.RefreshThrottler; import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.python.google.common.collect.Sets; +import org.sleuthkit.datamodel.Blackboard; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG; @@ -644,17 +645,31 @@ public class Artifacts { @Override protected List makeKeys() { try { - List arts; - arts = (filteringDSObjId > 0) - ? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifacts(type.getTypeID(), filteringDSObjId) - : Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID()); + List arts; + Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard(); + switch (this.type.getCategory()) { + + case ANALYSIS_RESULT: + arts = (filteringDSObjId > 0) + ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId) + : blackboard.getAnalysisResultsByType(type.getTypeID()); + case DATA_ARTIFACT: + default: + arts = (filteringDSObjId > 0) + ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId) + : blackboard.getDataArtifacts(type.getTypeID()); + } + for (BlackboardArtifact art : arts) { //Cache attributes while we are off the EDT. //See JIRA-5969 art.getAttributes(); } - return arts; + + @SuppressWarnings("unchecked") + List toRet = (List)(List)arts; + return toRet; } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS } catch (TskCoreException ex) { From 1ff145c80e9c07a007763d580bdfec30a0b74eaa Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 25 May 2021 17:25:03 -0400 Subject: [PATCH 46/78] Updated with changes per standup conversation --- .../sleuthkit/autopsy/casemodule/Case.java | 168 ++++++++++-------- .../autopsy/ingest/RunIngestAction.java | 2 +- .../autopsy/timeline/OpenTimelineAction.java | 2 +- 3 files changed, 92 insertions(+), 80 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 8e2e263cd9..0f608dc93f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -65,6 +65,7 @@ import javax.annotation.concurrent.ThreadSafe; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.apache.commons.lang3.StringUtils; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -507,6 +508,7 @@ public class Case { @Subscribe public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) { + hasData = true; for (OsAccount account : event.getOsAcounts()) { eventPublisher.publish(new OsAccountAddedEvent(account)); } @@ -521,6 +523,12 @@ public class Case { @Subscribe public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) { + try { + hasData = dbHasData(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex); + } + for (Long accountId : event.getOsAcountObjectIds()) { eventPublisher.publish(new OsAccountDeletedEvent(accountId)); } @@ -534,6 +542,7 @@ public class Case { */ @Subscribe public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) { + hasData = true; eventPublisher.publish(new HostsAddedEvent( event == null ? Collections.emptyList() : event.getHosts())); } @@ -558,6 +567,12 @@ public class Case { */ @Subscribe public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) { + try { + hasData = dbHasData(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex); + } + eventPublisher.publish(new HostsRemovedEvent( event == null ? Collections.emptyList() : event.getHosts())); } @@ -1036,7 +1051,6 @@ public class Case { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase(); openCaseAction = newCurrentCase::open; } - newCurrentCase.initializeDataListners(); newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); currentCase = newCurrentCase; logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS @@ -1671,16 +1685,20 @@ public class Case { } /** - * Queries whether or not the case has data, i.e., whether or not at least - * one data source has been added to the case. - * + * Returns true if there is any data in the case. + * * @return True or false. */ public boolean hasData() { return hasData; } - public boolean hasDataSources() { + /** + * Returns true if there is one or more data sources in the case. + * + * @return True or false. + */ + public boolean hasDataSource() { return hasDataSource; } @@ -1695,6 +1713,8 @@ public class Case { * notifyNewDataSource after the data source is added. */ public void notifyAddingDataSource(UUID eventId) { + hasDataSource = true; + hasData = true; eventPublisher.publish(new AddingDataSourceEvent(eventId)); } @@ -1947,6 +1967,8 @@ public class Case { String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS throw new TskCoreException(errorMsg, ex); } + hasData = true; + Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent); eventPublisher.publish(new ReportAddedEvent(report)); return report; @@ -1975,8 +1997,17 @@ public class Case { public void deleteReports(Collection reports) throws TskCoreException { for (Report report : reports) { this.caseDb.deleteReport(report); - eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null)); } + + try { + hasData = dbHasData(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex); + } + + for (Report report : reports) { + eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null)); + } } /** @@ -2727,13 +2758,12 @@ public class Case { String databaseName = metadata.getCaseDatabaseName(); if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString()); - initializeDataParameters(); } else if (UserPreferences.getIsMultiUserModeEnabled()) { caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory()); - initializeDataParameters(); } else { throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled()); } + updateDataParameters(); } catch (TskUnsupportedSchemaVersionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex); } catch (UserPreferencesException ex) { @@ -3511,22 +3541,6 @@ public class Case { } } - private long getDataSourceCount() throws TskCoreException { - long numDataSources = 0; - String query = "SELECT count(*) AS count FROM data_source_info"; - try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { - ResultSet resultSet = dbQuery.getResultSet(); - if (resultSet.next()) { - numDataSources = resultSet.getLong("count"); - } - } catch ( SQLException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - throw new TskCoreException("Error accessing case databse", ex); - } - - return numDataSources; - } - /** * Initialize the hasData and hasDataSource parameters by checking the * database. @@ -3538,68 +3552,66 @@ public class Case { * * @throws TskCoreException */ - private void initializeDataParameters() throws TskCoreException { - hasDataSource = getDataSourceCount() > 0; - - if(!hasDataSource) { - // Check to see if there are objects like os accounts or reports - // which both have object ids assocated with them. - String query = "SELECT count(*) AS count FROM tsk_objects"; - try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { - ResultSet resultSet = dbQuery.getResultSet(); - if (resultSet.next()) { - hasData = resultSet.getLong("count") > 0; - } - } catch ( SQLException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - throw new TskCoreException("Error accessing case databse", ex); - } + private void updateDataParameters() throws TskCoreException { + hasDataSource = dbHasDataSource(); + + if (!hasDataSource) { + hasData = dbHasData(); } else { hasData = true; } - - if(!hasData) { - // No object, but check to see if there are any hosts. - String query = "SELECT count(*) AS count FROM tsk_hosts"; - try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { - ResultSet resultSet = dbQuery.getResultSet(); - if (resultSet.next()) { - hasData = resultSet.getLong("count") > 0; - } - } catch ( SQLException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - throw new TskCoreException("Error accessing case databse", ex); - } - } - } + } /** - * Create the listeners, if needed, to update hasDataSource and hasData. + * Returns true of there are any data sources in the case database. + * + * @return True if this case as a data source. + * + * @throws TskCoreException */ - private void initializeDataListners() { - if(!hasDataSource) { - addEventTypeSubscriber(Collections.singleton(Case.Events.DATA_SOURCE_ADDED), new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - hasDataSource = true; - hasData = true; - } - }); - } - - if(!hasData) { - PropertyChangeListener listener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - hasData = true; - } - }; - - addEventTypeSubscriber(Collections.singleton(Case.Events.HOSTS_ADDED),listener); - addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED),listener); - addEventTypeSubscriber(Collections.singleton(Case.Events.REPORT_ADDED),listener); + private boolean dbHasDataSource() throws TskCoreException { + String query = "SELECT count(*) AS count FROM (SELECT * FROM data_source_info LIMIT 1)t"; + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + if (resultSet.next()) { + return resultSet.getLong("count") > 0; + } + return false; + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS + throw new TskCoreException("Error accessing case databse", ex); } } + + /** + * Returns true if the case has data. A case has data if there is at least + * one row in either the tsk_objects or tsk_hosts table. + * + * @return True if there is data in this case. + * + * @throws TskCoreException + */ + private boolean dbHasData() throws TskCoreException { + // The LIMIT 1 in the subquery should limit the data returned and + // make the overall query more efficent. + String query = "SELECT SUM(cnt) total FROM " + + "(SELECT COUNT(*) AS cnt FROM " + + "(SELECT * FROM tsk_objects LIMIT 1)t " + + "UNION ALL " + + "SELECT COUNT(*) AS cnt FROM " + + "(SELECT * FROM tsk_hosts LIMIT 1)r) s"; + try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + if (resultSet.next()) { + return resultSet.getLong("total") > 0; + } else { + return false; + } + } catch ( SQLException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS + throw new TskCoreException("Error accessing case databse", ex); + } + } /** * Defines the signature for case action methods that can be passed as diff --git a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java index d18faeee7b..962795f92f 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java @@ -85,7 +85,7 @@ public final class RunIngestAction extends CallableSystemAction implements Prese public boolean isEnabled() { try { Case openCase = Case.getCurrentCaseThrows(); - return openCase.hasData(); + return openCase.hasDataSource(); } catch (NoCurrentCaseException ex) { return false; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index fdd45d14a1..1c2209999a 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -104,7 +104,7 @@ public final class OpenTimelineAction extends CallableSystemAction { synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact, Interval timeSpan) throws TskCoreException { try { Case currentCase = Case.getCurrentCaseThrows(); - if (currentCase.hasData() == false) { + if (currentCase.hasDataSource() == false) { MessageNotifyUtil.Message.info(Bundle.OpenTimeLineAction_msgdlg_text()); logger.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS return; From 715e08a5347b9f4e8f2ff1c3adea21e9f29f1b63 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 25 May 2021 21:31:39 -0400 Subject: [PATCH 47/78] Update VMExtractorIngestModule.java Add check if mimetype exists first, it if does not then get mimetype and update abstractfile with mimetype detected. --- .../vmextractor/VMExtractorIngestModule.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java index f07042c999..a79b0f1a12 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java @@ -119,7 +119,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { try { // look for all VM files vmFiles = findVirtualMachineFiles(dataSource); - vmFiles = removeNonVHDFiles(vmFiles); + vmFiles = removeNonVMFiles(vmFiles); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error querying case database", ex); //NON-NLS return ProcessResult.ERROR; @@ -241,23 +241,35 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { } /** - * Check all the files and if a file is a vhd then check to make sure it is a valid vhd using the mimetype + * Check all the files and if a file is a vhd then check to make sure it is a valid vhd using the mimetype. We are not + * checking the mimetype for VMDK's at this point in time. + * * @param vmFiles List of virtual machine abstract files to look at + * * @return List of abstract files of virtual machine files. */ - private static List removeNonVHDFiles(List vmFiles) { + private static List removeNonVMFiles(List vmFiles) { List vFile = new ArrayList<>(); for (AbstractFile vmFile : vmFiles) { if (vmFile.getNameExtension().equalsIgnoreCase("vhd")) { - FileTypeDetector fileTypeDetector = null; - try { - fileTypeDetector = new FileTypeDetector(); - } catch (FileTypeDetector.FileTypeDetectorInitException ex) { - logger.log(Level.WARNING, String.format("Unable to create file type detector for determining MIME type for file %s with id of %d", vmFile.getName(), vmFile.getId())); - } - String mimeType = fileTypeDetector.getMIMEType(vmFile); - if (mimeType.equalsIgnoreCase("application/x-vhd")) { + String fileMimeType = vmFile.getMIMEType(); + if (fileMimeType == null) { + FileTypeDetector fileTypeDetector = null; + try { + fileTypeDetector = new FileTypeDetector(); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + logger.log(Level.WARNING, String.format("Unable to create file type detector for determining MIME type for file %s with id of %d", vmFile.getName(), vmFile.getId())); + } + fileMimeType = fileTypeDetector.getMIMEType(vmFile); + try { + vmFile.setMIMEType(fileMimeType); + vmFile.save(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to save mimetype of %s for file %s with id of %d", fileMimeType, vmFile.getName(), vmFile.getId())); + } + } + if (fileMimeType.equalsIgnoreCase("application/x-vhd")) { vFile.add(vmFile); } } else { From 90af83505a4f13986fc7ac1379a83ece382e6fc4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 08:13:36 -0400 Subject: [PATCH 48/78] fixes for special viewers --- .../autopsy/datamodel/Artifacts.java | 3 ++- .../autopsy/datamodel/EmailExtracted.java | 17 +++++++++-------- .../autopsy/datamodel/HashsetHits.java | 17 +++++++++-------- .../autopsy/datamodel/InterestingHits.java | 17 +++++++++-------- .../autopsy/datamodel/KeywordHits.java | 19 ++++++++++--------- .../autopsy/datamodel/accounts/Accounts.java | 9 +++++---- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java index 976cc7dc3d..796be557fd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java @@ -653,14 +653,15 @@ public class Artifacts { arts = (filteringDSObjId > 0) ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId) : blackboard.getAnalysisResultsByType(type.getTypeID()); + break; case DATA_ARTIFACT: default: arts = (filteringDSObjId > 0) ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId) : blackboard.getDataArtifacts(type.getTypeID()); + break; } - for (BlackboardArtifact art : arts) { //Cache attributes while we are off the EDT. //See JIRA-5969 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 357f6ec153..0acd6a8bf3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; +import org.sleuthkit.datamodel.DataArtifact; /** * Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree. @@ -161,7 +162,7 @@ public class EmailExtracted implements AutopsyVisitableItem { int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); String query = "SELECT \n" - + " art.artifact_id AS artifact_id,\n" + + " art.artifact_obj_id AS artifact_obj_id,\n" + " (SELECT value_text FROM blackboard_attributes attr\n" + " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n" + " LIMIT 1) AS value_text\n" @@ -176,14 +177,14 @@ public class EmailExtracted implements AutopsyVisitableItem { try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { - Long artifactId = resultSet.getLong("artifact_id"); + Long artifactObjId = resultSet.getLong("artifact_obj_id"); Map accountFolderMap = parsePath(resultSet.getString("value_text")); String account = accountFolderMap.get(MAIL_ACCOUNT); String folder = accountFolderMap.get(MAIL_FOLDER); Map> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>()); List messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>()); - messages.add(artifactId); + messages.add(artifactObjId); } } catch (TskCoreException | SQLException ex) { logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS @@ -499,7 +500,7 @@ public class EmailExtracted implements AutopsyVisitableItem { /** * Node representing mail folder content (mail messages) */ - private class MessageFactory extends BaseChildFactory implements Observer { + private class MessageFactory extends BaseChildFactory implements Observer { private final String accountName; private final String folderName; @@ -512,7 +513,7 @@ public class EmailExtracted implements AutopsyVisitableItem { } @Override - protected Node createNodeForKey(BlackboardArtifact art) { + protected Node createNodeForKey(DataArtifact art) { return new BlackboardArtifactNode(art); } @@ -522,13 +523,13 @@ public class EmailExtracted implements AutopsyVisitableItem { } @Override - protected List makeKeys() { - List keys = new ArrayList<>(); + protected List makeKeys() { + List keys = new ArrayList<>(); if (skCase != null) { emailResults.getArtifactIds(accountName, folderName).forEach((id) -> { try { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); + DataArtifact art = skCase.getBlackboard().getDataArtifactById(id); //Cache attributes while we are off the EDT. //See JIRA-5969 art.getAttributes(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 5906b01a78..e98b154ff0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -52,6 +52,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; +import org.sleuthkit.datamodel.AnalysisResult; /** * Hash set hits node support. Inner classes have all of the nodes in the tree. @@ -136,7 +137,7 @@ public class HashsetHits implements AutopsyVisitableItem { int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int artId = TSK_HASHSET_HIT.getTypeID(); - String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS + String query = "SELECT value_text,blackboard_artifacts.artifact_obj_id,attribute_type_id " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS @@ -150,11 +151,11 @@ public class HashsetHits implements AutopsyVisitableItem { synchronized (hashSetHitsMap) { while (resultSet.next()) { String setName = resultSet.getString("value_text"); //NON-NLS - long artifactId = resultSet.getLong("artifact_id"); //NON-NLS + long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS if (!hashSetHitsMap.containsKey(setName)) { hashSetHitsMap.put(setName, new HashSet<>()); } - hashSetHitsMap.get(setName).add(artifactId); + hashSetHitsMap.get(setName).add(artifactObjId); } } } catch (TskCoreException | SQLException ex) { @@ -380,10 +381,10 @@ public class HashsetHits implements AutopsyVisitableItem { /** * Creates the nodes for the hits in a given set. */ - private class HitFactory extends BaseChildFactory implements Observer { + private class HitFactory extends BaseChildFactory implements Observer { private final String hashsetName; - private final Map artifactHits = new HashMap<>(); + private final Map artifactHits = new HashMap<>(); private HitFactory(String hashsetName) { super(hashsetName); @@ -401,7 +402,7 @@ public class HashsetHits implements AutopsyVisitableItem { } @Override - protected Node createNodeForKey(BlackboardArtifact key) { + protected Node createNodeForKey(AnalysisResult key) { return new BlackboardArtifactNode(key); } @@ -411,13 +412,13 @@ public class HashsetHits implements AutopsyVisitableItem { } @Override - protected List makeKeys() { + protected List makeKeys() { if (skCase != null) { hashsetResults.getArtifactIds(hashsetName).forEach((id) -> { try { if (!artifactHits.containsKey(id)) { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); + AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); //Cache attributes while we are off the EDT. //See JIRA-5969 art.getAttributes(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 22525854ca..57f2524ff9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -51,6 +51,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; +import org.sleuthkit.datamodel.AnalysisResult; public class InterestingHits implements AutopsyVisitableItem { @@ -129,7 +130,7 @@ public class InterestingHits implements AutopsyVisitableItem { int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int artId = artType.getTypeID(); - String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS + String query = "SELECT value_text,blackboard_artifacts.artifact_obj_id,attribute_type_id " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS @@ -143,13 +144,13 @@ public class InterestingHits implements AutopsyVisitableItem { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { String value = resultSet.getString("value_text"); //NON-NLS - long artifactId = resultSet.getLong("artifact_id"); //NON-NLS + long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS if (!interestingItemsMap.containsKey(value)) { interestingItemsMap.put(value, new LinkedHashMap<>()); interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>()); interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>()); } - interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactId); + interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactObjId); } } } catch (TskCoreException | SQLException ex) { @@ -459,11 +460,11 @@ public class InterestingHits implements AutopsyVisitableItem { } } - private class HitFactory extends BaseChildFactory implements Observer { + private class HitFactory extends BaseChildFactory implements Observer { private final String setName; private final String typeName; - private final Map artifactHits = new HashMap<>(); + private final Map artifactHits = new HashMap<>(); private HitFactory(String setName, String typeName) { /** @@ -478,13 +479,13 @@ public class InterestingHits implements AutopsyVisitableItem { } @Override - protected List makeKeys() { + protected List makeKeys() { if (skCase != null) { interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { try { if (!artifactHits.containsKey(id)) { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); + AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); //Cache attributes while we are off the EDT. //See JIRA-5969 art.getAttributes(); @@ -501,7 +502,7 @@ public class InterestingHits implements AutopsyVisitableItem { } @Override - protected Node createNodeForKey(BlackboardArtifact art) { + protected Node createNodeForKey(AnalysisResult art) { return new BlackboardArtifactNode(art); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index eab8a07d3a..a129e3ff30 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -57,6 +57,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; +import org.sleuthkit.datamodel.AnalysisResult; /** * Keyword hits node support @@ -91,7 +92,7 @@ public class KeywordHits implements AutopsyVisitableItem { */ private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS + "blackboard_attributes.value_int32, "//NON-NLS - + "blackboard_attributes.artifact_id, " //NON-NLS + + "blackboard_artifacts.artifact_obj_id, " //NON-NLS + "blackboard_attributes.attribute_type_id "//NON-NLS + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS @@ -349,12 +350,12 @@ public class KeywordHits implements AutopsyVisitableItem { try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { - long artifactId = resultSet.getLong("artifact_id"); //NON-NLS + long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS String valueStr = resultSet.getString("value_text"); //NON-NLS //get the map of attributes for this artifact - Map attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> new LinkedHashMap<>()); + Map attributesByTypeMap = artifactIds.computeIfAbsent(artifactObjId, ai -> new LinkedHashMap<>()); if (StringUtils.isNotEmpty(valueStr)) { attributesByTypeMap.put(typeId, valueStr); } else { @@ -858,7 +859,7 @@ public class KeywordHits implements AutopsyVisitableItem { "KeywordHits.createNodeForKey.chgTime.name=ChangeTime", "KeywordHits.createNodeForKey.chgTime.displayName=Change Time", "KeywordHits.createNodeForKey.chgTime.desc=Change Time"}) - private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) { + private BlackboardArtifactNode createBlackboardArtifactNode(AnalysisResult art) { if (skCase == null) { return null; } @@ -905,12 +906,12 @@ public class KeywordHits implements AutopsyVisitableItem { /** * Creates nodes for individual files that had hits */ - private class HitsFactory extends BaseChildFactory implements Observer { + private class HitsFactory extends BaseChildFactory implements Observer { private final String keyword; private final String setName; private final String instance; - private final Map artifactHits = new HashMap<>(); + private final Map artifactHits = new HashMap<>(); private HitsFactory(String setName, String keyword, String instance) { /** @@ -926,12 +927,12 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected List makeKeys() { + protected List makeKeys() { if (skCase != null) { keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> { try { if (!artifactHits.containsKey(id)) { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); + AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); //Cache attributes while we are off the EDT. //See JIRA-5969 art.getAttributes(); @@ -948,7 +949,7 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected Node createNodeForKey(BlackboardArtifact art) { + protected Node createNodeForKey(AnalysisResult art) { return createBlackboardArtifactNode(art); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index 18136db99e..3b3cb0ce9f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -81,6 +81,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact.Type; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData.DbType; @@ -569,7 +570,7 @@ final public class Accounts implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { String query - = "SELECT blackboard_artifacts.artifact_id " //NON-NLS + = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS + " FROM blackboard_artifacts " //NON-NLS + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS @@ -581,7 +582,7 @@ final public class Accounts implements AutopsyVisitableItem { ResultSet rs = results.getResultSet();) { List tempList = new ArrayList<>(); while (rs.next()) { - tempList.add(rs.getLong("artifact_id")); // NON-NLS + tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS } list.addAll(tempList); } catch (TskCoreException | SQLException ex) { @@ -594,7 +595,7 @@ final public class Accounts implements AutopsyVisitableItem { @Override protected Node[] createNodesForKey(Long t) { try { - return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))}; + return new Node[]{new BlackboardArtifactNode(skCase.getBlackboard().getDataArtifactById(t))}; } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex); return new Node[0]; @@ -1520,7 +1521,7 @@ final public class Accounts implements AutopsyVisitableItem { } try { - BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID); + DataArtifact art = skCase.getBlackboard().getDataArtifactById(artifactID); return new Node[]{new AccountArtifactNode(art)}; } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS From cde4661260a88dcdf16a6e9fbf9a2a4ca0ee2a7f Mon Sep 17 00:00:00 2001 From: apriestman Date: Wed, 26 May 2021 08:28:12 -0400 Subject: [PATCH 49/78] Shorten name of EFE folder --- .../EmbeddedFileExtractorIngestModule.java | 4 ++-- .../EmbeddedFileExtractorModuleFactory.java | 4 ++++ .../ExtractArchiveWithPasswordAction.java | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 21b5fc5ee7..44b85ae320 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -94,8 +94,8 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda * */ Case currentCase = Case.getCurrentCase(); - String moduleDirAbsolute = Paths.get(currentCase.getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); - String moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); + String moduleDirAbsolute = Paths.get(currentCase.getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getOutputFolderName()).toString(); + String moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getOutputFolderName()).toString(); if (refCounter.incrementAndGet(jobId) == 1) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorModuleFactory.java index 57534287a4..cdf3335230 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorModuleFactory.java @@ -45,6 +45,10 @@ public class EmbeddedFileExtractorModuleFactory extends IngestModuleFactoryAdapt public String getModuleDisplayName() { return getModuleName(); } + + static String getOutputFolderName() { + return "EFE"; + } @Override public String getModuleDescription() { diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ExtractArchiveWithPasswordAction.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ExtractArchiveWithPasswordAction.java index e080a27c9d..267435ce14 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ExtractArchiveWithPasswordAction.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ExtractArchiveWithPasswordAction.java @@ -112,8 +112,8 @@ public class ExtractArchiveWithPasswordAction extends AbstractAction { protected Boolean doInBackground() { boolean done = false; try { - String moduleDirRelative = Paths.get(Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); - String moduleDirAbsolute = Paths.get(Case.getCurrentCaseThrows().getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); + String moduleDirRelative = Paths.get(Case.getCurrentCaseThrows().getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getOutputFolderName()).toString(); + String moduleDirAbsolute = Paths.get(Case.getCurrentCaseThrows().getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getOutputFolderName()).toString(); /* * Construct a file type detector. */ From 55c547e0b5a6c08b2c8ae61328cb9e2e1716f49c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 09:12:06 -0400 Subject: [PATCH 50/78] get dataartifact from lookup --- .../autopsy/contentviewers/Metadata.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index e17c2c6317..fce395de95 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.Component; import java.awt.Cursor; -import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; @@ -36,9 +35,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; -import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.FsContent; @@ -238,20 +237,11 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { }) @Override public String getTitle(Node node) { - if (node != null) { - Collection artifacts = node.getLookup().lookupAll(BlackboardArtifact.class); - for (BlackboardArtifact art : artifacts) { - try { - if (art != null && art.getType().getCategory() == Category.DATA_ARTIFACT) { - return Bundle.Metadata_dataArtifactTitle(); - } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Unable to get artifact type for artifact with id: " + art.getArtifactID(), ex); - } - } + if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) { + return Bundle.Metadata_dataArtifactTitle(); + } else { + return NbBundle.getMessage(this.getClass(), "Metadata.title"); } - - return NbBundle.getMessage(this.getClass(), "Metadata.title"); } From c81b5a0a2b49596ef987230e99df5d23aa4585a5 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 09:58:55 -0400 Subject: [PATCH 51/78] fix --- .../autopsy/datamodel/BlackboardArtifactNode.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 2278f6d5c8..d993d50e73 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -45,7 +45,6 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -82,7 +81,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; -import org.sleuthkit.datamodel.DataArtifact; +import org.sleuthkit.datamodel.AnalysisResult; /** * A BlackboardArtifactNode is an AbstractNode implementation that can be used @@ -441,8 +440,8 @@ public class BlackboardArtifactNode extends AbstractContentNode Date: Wed, 26 May 2021 10:15:31 -0400 Subject: [PATCH 52/78] Removed unused imports --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 0f608dc93f..6c4ede3d99 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -26,7 +26,6 @@ import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; @@ -65,7 +64,6 @@ import javax.annotation.concurrent.ThreadSafe; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; From d18568b49c04c517523b71a2e2d480e53db3bf89 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 10:58:35 -0400 Subject: [PATCH 53/78] fix --- .../autopsy/directorytree/DataResultFilterNode.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 03f493d6d5..65fb6f850b 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -359,14 +359,16 @@ public class DataResultFilterNode extends FilterNode { actionsList.add(ExtractAction.getInstance()); actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator - actionsList.add(AddContentTagAction.getInstance()); - // don't show AddBlackboardArtifactTagAction for data artifacts. - if (n.getLookup().lookupAll(DataArtifact.class).isEmpty()) { - actionsList.add(AddBlackboardArtifactTagAction.getInstance()); + // don't show AddContentTagAction for data artifacts. + if (!(ban.getArtifact() instanceof DataArtifact)) { + actionsList.add(AddContentTagAction.getInstance()); } - if (selectedFilesList.size() == 1) { + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); + + // don't show DeleteFileContentTagAction for data artifacts. + if ((!(ban.getArtifact() instanceof DataArtifact)) && (selectedFilesList.size() == 1)) { actionsList.add(DeleteFileContentTagAction.getInstance()); } } else { From 951d8ce02a9d79eeffcb29ec0d8ef18b432d9373 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 13:03:32 -0400 Subject: [PATCH 54/78] updates to DataArtifactContentViewer --- .../autopsy/corecomponents/Bundle.properties | 22 +-- .../corecomponents/Bundle.properties-MERGED | 26 +-- .../corecomponents/Bundle_ja.properties | 26 +-- ...ct.form => DataArtifactContentViewer.form} | 42 +++-- ...ct.java => DataArtifactContentViewer.java} | 160 +++++++++--------- .../autopsy/datamodel/FileTypes.java | 10 ++ 6 files changed, 158 insertions(+), 128 deletions(-) rename Core/src/org/sleuthkit/autopsy/corecomponents/{DataContentViewerArtifact.form => DataArtifactContentViewer.form} (90%) rename Core/src/org/sleuthkit/autopsy/corecomponents/{DataContentViewerArtifact.java => DataArtifactContentViewer.java} (82%) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 77789fddb8..584ba1dcd1 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -23,13 +23,13 @@ INDEX_FOR_LOCAL_HELP=/docs/index.html LBL_Close=Close DataContentViewerHex.copyMenuItem.text=Copy DataContentViewerHex.selectAllMenuItem.text=Select All -DataContentViewerArtifact.totalPageLabel.text=100 -DataContentViewerArtifact.prevPageButton.text= -DataContentViewerArtifact.pageLabel2.text=Result -DataContentViewerArtifact.nextPageButton.text= -DataContentViewerArtifact.currentPageLabel.text=1 -DataContentViewerArtifact.ofLabel.text=of -DataContentViewerArtifact.pageLabel.text=Result: +DataArtifactContentViewer.totalPageLabel.text=100 +DataArtifactContentViewer.prevPageButton.text= +DataArtifactContentViewer.pageLabel2.text=Result +DataArtifactContentViewer.nextPageButton.text= +DataArtifactContentViewer.currentPageLabel.text=1 +DataArtifactContentViewer.ofLabel.text=of +DataArtifactContentViewer.pageLabel.text=Result: AdvancedConfigurationDialog.applyButton.text=OK DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageLabel.text=Go to Page: @@ -44,10 +44,10 @@ DataResultViewerThumbnail.filePathLabel.text=\ \ \ DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel -DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... -DataContentViewerArtifact.errorText=Error retrieving result -DataContentViewerArtifact.title=Data Artifacts -DataContentViewerArtifact.toolTip=Displays Results associated with the file +DataArtifactContentViewer.waitText=Retrieving and preparing data, please wait... +DataArtifactContentViewer.errorText=Error retrieving result +DataArtifactContentViewer.title=Data Artifacts +DataArtifactContentViewer.toolTip=Displays Results associated with the file DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.err=Invalid page number DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 9fc731013d..0636340b0b 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -30,8 +30,8 @@ CTL_DataContentAction=DataContent CTL_DataContentTopComponent=Data Content CTL_OfflineHelpAction=Offline Autopsy Documentation CTL_OnlineHelpAction=Online Autopsy Documentation -DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database -DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database +DataArtifactContentViewer.failedToGetAttributes.message=Failed to get some or all attributes from case database +DataArtifactContentViewer.failedToGetSourcePath.message=Failed to get source file path from case database DataContentViewerHex.copyingFile=Copying file to open in HxD... DataContentViewerHex.launchError=Unable to launch HxD Editor. Please specify the HxD install location in Tools -> Options -> External Viewer DataContentViewerHex_loading_text=Loading hex from file... @@ -88,13 +88,13 @@ INDEX_FOR_LOCAL_HELP=/docs/index.html LBL_Close=Close DataContentViewerHex.copyMenuItem.text=Copy DataContentViewerHex.selectAllMenuItem.text=Select All -DataContentViewerArtifact.totalPageLabel.text=100 -DataContentViewerArtifact.prevPageButton.text= -DataContentViewerArtifact.pageLabel2.text=Result -DataContentViewerArtifact.nextPageButton.text= -DataContentViewerArtifact.currentPageLabel.text=1 -DataContentViewerArtifact.ofLabel.text=of -DataContentViewerArtifact.pageLabel.text=Result: +DataArtifactContentViewer.totalPageLabel.text=100 +DataArtifactContentViewer.prevPageButton.text= +DataArtifactContentViewer.pageLabel2.text=Result +DataArtifactContentViewer.nextPageButton.text= +DataArtifactContentViewer.currentPageLabel.text=1 +DataArtifactContentViewer.ofLabel.text=of +DataArtifactContentViewer.pageLabel.text=Result: AdvancedConfigurationDialog.applyButton.text=OK DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageLabel.text=Go to Page: @@ -109,10 +109,10 @@ DataResultViewerThumbnail.filePathLabel.text=\ \ \ DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel -DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... -DataContentViewerArtifact.errorText=Error retrieving result -DataContentViewerArtifact.title=Data Artifacts -DataContentViewerArtifact.toolTip=Displays Results associated with the file +DataArtifactContentViewer.waitText=Retrieving and preparing data, please wait... +DataArtifactContentViewer.errorText=Error retrieving result +DataArtifactContentViewer.title=Data Artifacts +DataArtifactContentViewer.toolTip=Displays Results associated with the file DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.err=Invalid page number DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties index 9717d32fbb..1c6c191439 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties @@ -59,19 +59,19 @@ CTL_OnlineHelpAction=Autopsy\u30aa\u30f3\u30e9\u30a4\u30f3\u30c9\u30ad\u30e5\u30 CriterionChooser.ascendingRadio.text=\u25b2 \u6607\u9806\n CriterionChooser.descendingRadio.text=\u25bc \u964d\u9806 CriterionChooser.removeButton.text=\u524a\u9664 -DataContentViewerArtifact.currentPageLabel.text=1 -DataContentViewerArtifact.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f -DataContentViewerArtifact.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -DataContentViewerArtifact.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -DataContentViewerArtifact.nextPageButton.text= -DataContentViewerArtifact.ofLabel.text=/ -DataContentViewerArtifact.pageLabel.text=\u7d50\u679c\: -DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c -DataContentViewerArtifact.prevPageButton.text= -DataContentViewerArtifact.title=\u7d50\u679c -DataContentViewerArtifact.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a -DataContentViewerArtifact.totalPageLabel.text=100 -DataContentViewerArtifact.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... +DataArtifactContentViewer.currentPageLabel.text=1 +DataArtifactContentViewer.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f +DataArtifactContentViewer.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +DataArtifactContentViewer.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +DataArtifactContentViewer.nextPageButton.text= +DataArtifactContentViewer.ofLabel.text=/ +DataArtifactContentViewer.pageLabel.text=\u7d50\u679c\: +DataArtifactContentViewer.pageLabel2.text=\u7d50\u679c +DataArtifactContentViewer.prevPageButton.text= +DataArtifactContentViewer.title=\u7d50\u679c +DataArtifactContentViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a +DataArtifactContentViewer.totalPageLabel.text=100 +DataArtifactContentViewer.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... DataContentViewerHex.copyMenuItem.text=\u30b3\u30d4\u30fc DataContentViewerHex.copyingFile=HxD\u3067\u958b\u304f\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... DataContentViewerHex.currentPageLabel.text_1=1 diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.form similarity index 90% rename from Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form rename to Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.form index 8b555abbc0..3b33fc6d9f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.form @@ -2,8 +2,11 @@
+ + + - + @@ -21,33 +24,42 @@ - + - + - + - + + + + - + + + + + + + - + @@ -56,7 +68,7 @@ - + @@ -74,7 +86,7 @@ - + @@ -86,7 +98,7 @@ - + @@ -104,7 +116,7 @@ - + @@ -119,7 +131,7 @@ - + @@ -148,7 +160,7 @@ - + @@ -159,7 +171,7 @@ - + @@ -169,7 +181,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java rename to Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java index e1ce4c264a..24eef87679 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java @@ -32,19 +32,22 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import java.util.Collections; import java.util.HashSet; -import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer; +import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.Category.ANALYSIS_RESULT; +import static org.sleuthkit.datamodel.BlackboardArtifact.Category.DATA_ARTIFACT; +import org.sleuthkit.datamodel.DataArtifact; /** - * Instances of this class display the BlackboardArtifacts associated with the + * Instances of this class display the DataArtifact associated with the * Content represented by a Node. * * It goes through a list of known ArtifactContentViewer to find a viewer that @@ -52,17 +55,17 @@ import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifact */ @ServiceProvider(service = DataContentViewer.class, position = 7) @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { +public class DataArtifactContentViewer extends javax.swing.JPanel implements DataContentViewer { private static final long serialVersionUID = 1L; @NbBundle.Messages({ - "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database", - "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database" + "DataArtifactContentViewer.failedToGetSourcePath.message=Failed to get source file path from case database", + "DataArtifactContentViewer.failedToGetAttributes.message=Failed to get some or all attributes from case database" }) - private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName()); - private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText"); - private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText"); + private final static Logger logger = Logger.getLogger(DataArtifactContentViewer.class.getName()); + private final static String WAIT_TEXT = NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.waitText"); + private final static String ERROR_TEXT = NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.errorText"); // Value to return in isPreferred if this viewer is less preferred. private static final int LESS_PREFERRED = 3; @@ -72,12 +75,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed. private int currentPage = 1; private final Object lock = new Object(); - private List artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents() + private List artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents() private SwingWorker currentTask; // Accessed by multiple threads, use startNewTask() private final Collection knowArtifactViewers = new HashSet<>(Lookup.getDefault().lookupAll(ArtifactContentViewer.class)); - public DataContentViewerArtifact() { + public DataArtifactContentViewer() { initComponents(); @@ -94,8 +97,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; - jScrollPane1 = new javax.swing.JScrollPane(); - jPanel1 = new javax.swing.JPanel(); + scrollPane = new javax.swing.JScrollPane(); + menuBar = new javax.swing.JPanel(); totalPageLabel = new javax.swing.JLabel(); ofLabel = new javax.swing.JLabel(); currentPageLabel = new javax.swing.JLabel(); @@ -107,15 +110,19 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); artifactContentPanel = new javax.swing.JPanel(); - setPreferredSize(new java.awt.Dimension(100, 58)); + setMinimumSize(new java.awt.Dimension(300, 60)); + setPreferredSize(new java.awt.Dimension(300, 60)); - jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + scrollPane.setPreferredSize(new java.awt.Dimension(6, 60)); - jPanel1.setPreferredSize(new java.awt.Dimension(620, 58)); - jPanel1.setLayout(new java.awt.GridBagLayout()); + menuBar.setMaximumSize(null); + menuBar.setMinimumSize(null); + menuBar.setPreferredSize(null); + menuBar.setLayout(new java.awt.GridBagLayout()); - totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N + totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.totalPageLabel.text")); // NOI18N totalPageLabel.setMaximumSize(null); totalPageLabel.setPreferredSize(null); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -124,18 +131,18 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); - jPanel1.add(totalPageLabel, gridBagConstraints); + menuBar.add(totalPageLabel, gridBagConstraints); - ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N + ofLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.ofLabel.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); - jPanel1.add(ofLabel, gridBagConstraints); + menuBar.add(ofLabel, gridBagConstraints); - currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N + currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.currentPageLabel.text")); // NOI18N currentPageLabel.setMaximumSize(null); currentPageLabel.setPreferredSize(null); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -144,19 +151,19 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 7, 0, 0); - jPanel1.add(currentPageLabel, gridBagConstraints); + menuBar.add(currentPageLabel, gridBagConstraints); - pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N + pageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.pageLabel.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); - jPanel1.add(pageLabel, gridBagConstraints); + menuBar.add(pageLabel, gridBagConstraints); nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N - nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N + nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.nextPageButton.text")); // NOI18N nextPageButton.setBorderPainted(false); nextPageButton.setContentAreaFilled(false); nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N @@ -173,9 +180,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat gridBagConstraints.gridy = 0; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 35, 0); - jPanel1.add(nextPageButton, gridBagConstraints); + menuBar.add(nextPageButton, gridBagConstraints); - pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N + pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.pageLabel2.text")); // NOI18N pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14)); pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14)); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -183,11 +190,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(3, 41, 0, 0); - jPanel1.add(pageLabel2, gridBagConstraints); + gridBagConstraints.insets = new java.awt.Insets(3, 30, 0, 0); + menuBar.add(pageLabel2, gridBagConstraints); prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N - prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N + prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.prevPageButton.text")); // NOI18N prevPageButton.setBorderPainted(false); prevPageButton.setContentAreaFilled(false); prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N @@ -204,22 +211,22 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat gridBagConstraints.gridy = 0; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 5, 35, 0); - jPanel1.add(prevPageButton, gridBagConstraints); + menuBar.add(prevPageButton, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 8; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 8); - jPanel1.add(artifactLabel, gridBagConstraints); + menuBar.add(artifactLabel, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 7; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 0.1; - jPanel1.add(filler1, gridBagConstraints); + menuBar.add(filler1, gridBagConstraints); - jScrollPane1.setViewportView(jPanel1); + scrollPane.setViewportView(menuBar); artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel)); @@ -227,15 +234,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 738, Short.MAX_VALUE) + .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)) + .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 30, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -258,13 +265,13 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat private javax.swing.JLabel artifactLabel; private javax.swing.JLabel currentPageLabel; private javax.swing.Box.Filler filler1; - private javax.swing.JPanel jPanel1; - private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JPanel menuBar; private javax.swing.JButton nextPageButton; private javax.swing.JLabel ofLabel; private javax.swing.JLabel pageLabel; private javax.swing.JLabel pageLabel2; private javax.swing.JButton prevPageButton; + private javax.swing.JScrollPane scrollPane; private javax.swing.JLabel totalPageLabel; // End of variables declaration//GEN-END:variables @@ -308,17 +315,17 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat @Override public String getTitle() { - return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.title"); + return NbBundle.getMessage(this.getClass(), "DataArtifactContentViewer.title"); } @Override public String getToolTip() { - return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.toolTip"); + return NbBundle.getMessage(this.getClass(), "DataArtifactContentViewer.toolTip"); } @Override public DataContentViewer createInstance() { - return new DataContentViewerArtifact(); + return new DataArtifactContentViewer(); } @Override @@ -338,11 +345,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat } for (Content content : node.getLookup().lookupAll(Content.class)) { - if ((content != null) && (!(content instanceof BlackboardArtifact))) { + if ((content != null) && (!(content instanceof DataArtifact)) && (!(content instanceof AnalysisResult))) { try { - return content.getAllArtifactsCount() > 0; + return content.hasDataArtifacts(); } catch (TskException ex) { - logger.log(Level.SEVERE, "Couldn't get count of BlackboardArtifacts for content", ex); //NON-NLS + logger.log(Level.SEVERE, "Couldn't get count of DataArtifacts for content", ex); //NON-NLS } } } @@ -352,7 +359,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat @Override public int isPreferred(Node node) { // get the artifact from the lookup - BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); + DataArtifact artifact = node.getLookup().lookup(DataArtifact.class); if (artifact == null) { return LESS_PREFERRED; } @@ -375,18 +382,10 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat return LESS_PREFERRED; } - switch (artifactType.getCategory()) { - // data artifacts should be more preferred - case DATA_ARTIFACT: - return MORE_PREFERRED; - // everything else is less preferred - case ANALYSIS_RESULT: - default: - return LESS_PREFERRED; - } + return MORE_PREFERRED; } - private ArtifactContentViewer getSupportingViewer(BlackboardArtifact artifact) { + private ArtifactContentViewer getSupportingViewer(DataArtifact artifact) { for (ArtifactContentViewer viewer : knowArtifactViewers) { if (viewer.isSupported(artifact)) { return viewer; @@ -403,10 +402,10 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat int numberOfPages; int currentPage; - BlackboardArtifact artifact; + DataArtifact artifact; String errorMsg; - ViewUpdate(int numberOfPages, int currentPage, BlackboardArtifact artifact) { + ViewUpdate(int numberOfPages, int currentPage, DataArtifact artifact) { this.currentPage = currentPage; this.numberOfPages = numberOfPages; this.artifact = artifact; @@ -442,7 +441,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat if (viewUpdate.artifact != null) { artifactLabel.setText(viewUpdate.artifact.getDisplayName()); - BlackboardArtifact artifact = viewUpdate.artifact; + DataArtifact artifact = viewUpdate.artifact; ArtifactContentViewer viewer = this.getSupportingViewer(artifact); viewer.setArtifact(artifact); @@ -484,7 +483,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat * @param artifactList A list of ResultsTableArtifact representations of * artifacts. */ - private void setArtifactContents(List artifactList) { + private void setArtifactContents(List artifactList) { synchronized (lock) { this.artifactTableContents = artifactList; } @@ -495,11 +494,22 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat * * @return A list of artifacts. */ - private List getArtifactContents() { + private List getArtifactContents() { synchronized (lock) { return Collections.unmodifiableList(artifactTableContents); } } + + /** + * Metric for determining if content is parent source content. + * @param content The content. + * @return True if this content should be used for source content. + */ + private static boolean isSourceContent(Content content) { + return (content != null) && + (!(content instanceof DataArtifact)) && + (!(content instanceof AnalysisResult)); + } /** * Instances of this class use a background thread to generate a ViewUpdate @@ -520,20 +530,18 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat // blackboard artifact, if any. Lookup lookup = selectedNode.getLookup(); - // Get the content. We may get BlackboardArtifacts, ignore those here. - ArrayList artifacts = new ArrayList<>(); + // Get the content. We may get DataArtifacts, ignore those here. + List artifacts = Collections.emptyList(); Collection contents = lookup.lookupAll(Content.class); if (contents.isEmpty()) { return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT); } - Content underlyingContent = null; for (Content content : contents) { - if ((content != null) && (!(content instanceof BlackboardArtifact))) { + if (isSourceContent(content)) { // Get all of the blackboard artifacts associated with the content. These are what this // viewer displays. try { - artifacts = content.getAllArtifacts(); - underlyingContent = content; + artifacts = content.getAllDataArtifacts(); break; } catch (TskException ex) { logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS @@ -547,15 +555,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat } // Build the new artifact contents cache. - ArrayList artifactContents = new ArrayList<>(); - for (BlackboardArtifact artifact : artifacts) { + ArrayList artifactContents = new ArrayList<>(); + for (DataArtifact artifact : artifacts) { artifactContents.add(artifact); } - // If the node has an underlying blackboard artifact, show it. If not, + // If the node has an underlying data artifact, show it. If not, // show the first artifact. int index = 0; - BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class); + DataArtifact artifact = lookup.lookup(DataArtifact.class); if (artifact != null) { index = artifacts.indexOf(artifact); if (index == -1) { @@ -567,7 +575,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { long assocArtifactId = attr.getValueLong(); int assocArtifactIndex = -1; - for (BlackboardArtifact art : artifacts) { + for (DataArtifact art : artifacts) { if (assocArtifactId == art.getArtifactID()) { assocArtifactIndex = artifacts.indexOf(art); break; @@ -636,14 +644,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat protected ViewUpdate doInBackground() { // Get the artifact content to display from the cache. Note that one must be subtracted from the // page index to get the corresponding artifact content index. - List artifactContents = getArtifactContents(); + List artifactContents = getArtifactContents(); // It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation. if (isCancelled()) { return null; } - BlackboardArtifact artifactContent = artifactContents.get(pageIndex - 1); + DataArtifact artifactContent = artifactContents.get(pageIndex - 1); return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index e5d374f6f0..822ff78be4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -481,5 +481,15 @@ public final class FileTypes implements AutopsyVisitableItem { public List getAllAnalysisResults() throws TskCoreException { return content.getAllAnalysisResults(); } + + @Override + public List getAllDataArtifacts() throws TskCoreException { + return content.getAllDataArtifacts(); + } + + @Override + public boolean hasDataArtifacts() throws TskCoreException { + return content.hasDataArtifacts(); + } } } From 9975705b69b1435e1470c51d390946d938d9f134 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 26 May 2021 13:50:23 -0400 Subject: [PATCH 55/78] Fixed NPE --- .../sleuthkit/autopsy/contentviewers/SQLiteViewer.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index e3b4c7ac7d..7772be7d29 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -354,10 +354,12 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList.removeAllItems(); numEntriesField.setText(""); - try { - viewReader.close(); - } catch (SQLiteTableReaderException ex) { - //Could not successfully close the reader, nothing we can do to recover. + if(viewReader != null) { + try { + viewReader.close(); + } catch (SQLiteTableReaderException ex) { + //Could not successfully close the reader, nothing we can do to recover. + } } row = new LinkedHashMap<>(); pageOfTableRows = new ArrayList<>(); From 177b1d02922bcfea6229ce5573d1b5914f22482c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 14:26:54 -0400 Subject: [PATCH 56/78] rendering --- .../AnalysisResultsContentViewer.form | 51 ++++- .../AnalysisResultsContentViewer.java | 189 ++++++++++++++++-- 2 files changed, 219 insertions(+), 21 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form index 5f3eab1a5f..fb2b7c3d9b 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form @@ -1,6 +1,17 @@ + + + + + + + + + + + @@ -16,13 +27,49 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index 7416ef6d69..aced0974c6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.contentviewers.analysisresults; +import java.awt.Color; import java.awt.Component; +import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -30,10 +32,18 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.swing.JLabel; +import javax.swing.text.EditorKit; +import javax.swing.text.html.HTMLEditorKit; import org.apache.commons.lang3.tuple.Pair; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.contentviewers.Utilities; +import org.sleuthkit.autopsy.contentviewers.application.Annotations; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; @@ -47,7 +57,48 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); private static final int PREFERRED_VALUE = 6; - + private static final String EMPTY_HTML = ""; + + private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); + private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize(); + private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground(); + + // html stylesheet classnames for components + public static final String MESSAGE_CLASSNAME = "message"; + public static final String SUBSECTION_CLASSNAME = "subsection"; + public static final String SUBHEADER_CLASSNAME = "subheader"; + public static final String SECTION_CLASSNAME = "section"; + public static final String HEADER_CLASSNAME = "header"; + + // how big the subheader should be + private static final int SUBHEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 12 / 11; + + // how big the header should be + private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 14 / 11; + + // the subsection indent + private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE; + + // spacing occurring after an item + private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE * 2; + private static final int DEFAULT_SUBSECTION_SPACING = DEFAULT_FONT_SIZE / 2; + private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; + + // additional styling for components + private static final String STYLE_SHEET_RULE + = String.format(" .%s { font-family: %s; font-size: %dpt; font-style:italic; margin: 0px; padding: 0px; } ", + Annotations.MESSAGE_CLASSNAME, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE) + + String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; margin-top: %dpx; padding: 0px; } ", + Annotations.SUBHEADER_CLASSNAME, DEFAULT_FONT_FAMILY, SUBHEADER_FONT_SIZE, DEFAULT_SUBSECTION_SPACING) + + String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; padding: 0px; } ", + Annotations.HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + + String.format(" td { vertical-align: top; font-family: %s; font-size:%dpt; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px;} ", + DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) + + String.format(" th { vertical-align: top; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px} ", + DEFAULT_FONT_SIZE, CELL_SPACING) + + String.format(" .%s { margin: %dpx 0px; padding-left: %dpx; } ", Annotations.SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_SPACING, DEFAULT_SUBSECTION_LEFT_PAD) + + String.format(" .%s { margin-bottom: %dpx; } ", Annotations.SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); + private static Optional getAggregateScore(Collection analysisResults) { return analysisResults.stream() .map(AnalysisResult::getScore) @@ -57,7 +108,6 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private static String normalizeAttr(String originalAttrStr) { return (originalAttrStr == null) ? "" : originalAttrStr.trim(); } - private static class ResultDisplayAttributes { @@ -161,7 +211,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements Map allAnalysisResults = new HashMap<>(); Optional selectedResult = Optional.empty(); - + AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); if (abstractFile != null) { try { @@ -175,41 +225,109 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); if (analysisResults.size() > 0) { - - List filteredResults = analysisResults.stream() + + List filteredResults = analysisResults.stream() .filter(ar -> ar != null && ar.getScore() != null) .collect(Collectors.toList()); - + filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); - + selectedResult = filteredResults.stream() - .max((a,b) -> a.getScore().compareTo(b.getScore())); + .max((a, b) -> a.getScore().compareTo(b.getScore())); } - + return new NodeAnalysisResults(allAnalysisResults.values(), selectedResult); } - - private static void render(Node node) { + + private static void refresh(Node node) { NodeAnalysisResults nodeResults = getAnalysisResults(node); List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); List displayAttributes = getDisplayAttributes(orderedAnalysisResults); - - // GVDTODO - + + Optional aggregateScore = displayAttributes.stream() + .findFirst() + .flatMap(dispAttrs -> Optional.ofNullable(dispAttrs.getAnalysisResult().getScore())); + + render(displayAttributes, aggregateScore); + Optional selectedResult = nodeResults.getSelectedResult(); if (selectedResult.isPresent()) { // GVDTODO } } + private static Document render(List displayAttributes, Optional aggregateScore) { + Document html = Jsoup.parse(EMPTY_HTML); + Element body = html.getElementsByTag("body").first(); + + if (aggregateScore.isPresent()) { + appendAggregateScore(body, aggregateScore.get()); + } + + for (int idx = 0; idx < displayAttributes.size(); idx++) { + ResultDisplayAttributes resultAttrs = displayAttributes.get(idx); + appendResult(body, idx, resultAttrs); + } + + return html; + } + + @Messages("AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score") + private static void appendAggregateScore(Element body, Score score) { + appendSection(body, MessageFormat.format("{0}: {1}", + Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), + score.getSignificance().getDisplayName())); + } + + @Messages({"# {0} - analysisResultsNumber", + "AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0}" + }) + private static void appendResult(Element parent, int index, ResultDisplayAttributes attrs) { + Element sectionElement = appendSection(parent, Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1)); + + Element table = parent.appendElement("table"); + Element tableBody = table.appendElement("tbody"); + + for (Pair keyVal : attrs.getAttributesToDisplay()) { + Element row = tableBody.appendElement("tr"); + String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":"; + row.appendElement("td").text(keyString); + String valueString = keyVal.getValue() == null ? "" : keyVal.getValue(); + row.appendElement("td").text(valueString); + } + } + + /** + * Appends a new section with a section header to the parent element. + * + * @param parent The element to append this section to. + * @param headerText The text for the section. + * + * @return The div for the new section. + */ + private static Element appendSection(Element parent, String headerText) { + Element sectionDiv = parent.appendElement("div"); + sectionDiv.attr("class", SECTION_CLASSNAME); + Element header = sectionDiv.appendElement("h1"); + header.text(headerText); + header.attr("class", HEADER_CLASSNAME); + return sectionDiv; + } + private Node selectedNode; - - + /** * Creates new form AnalysisResultsContentViewer */ public AnalysisResultsContentViewer() { initComponents(); + Utilities.configureTextPaneAsHtml(textPane); + // get html editor kit and apply additional style rules + EditorKit editorKit = textPane.getEditorKit(); + if (editorKit instanceof HTMLEditorKit) { + HTMLEditorKit htmlKit = (HTMLEditorKit) editorKit; + htmlKit.getStyleSheet().addRule(STYLE_SHEET_RULE); + } } @NbBundle.Messages({ @@ -250,7 +368,23 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Override public boolean isSupported(Node node) { - return getAnalysisResults(node).getAnalysisResults().size() > 0; + if (node == null) { + return false; + } + + AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); + if (abstractFile != null) { + try { + if (abstractFile.hasAnalysisResults()) { + return true; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); + } + } + + Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); + return (!analysisResults.isEmpty()); } @Override @@ -267,19 +401,36 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // //GEN-BEGIN:initComponents private void initComponents() { + javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); + textPane = new javax.swing.JTextPane(); + + setMaximumSize(null); + setMinimumSize(new java.awt.Dimension(250, 250)); + setPreferredSize(null); + + scrollPane.setMaximumSize(null); + scrollPane.setMinimumSize(null); + scrollPane.setPreferredSize(null); + + textPane.setMaximumSize(null); + textPane.setMinimumSize(null); + textPane.setPreferredSize(null); + scrollPane.setViewportView(textPane); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 400, Short.MAX_VALUE) + .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 300, Short.MAX_VALUE) + .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextPane textPane; // End of variables declaration//GEN-END:variables } From 9ad60a6f889c8282febd56170775da438e47de69 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 14:59:19 -0400 Subject: [PATCH 57/78] public api updates --- .../DataArtifactContentViewer.java | 19 ++++++++++++++----- .../autopsy/datamodel/FileTypes.java | 5 ----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java index 24eef87679..662df44a22 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java @@ -38,12 +38,12 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import java.util.Collections; import java.util.HashSet; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer; import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.Category.ANALYSIS_RESULT; -import static org.sleuthkit.datamodel.BlackboardArtifact.Category.DATA_ARTIFACT; import org.sleuthkit.datamodel.DataArtifact; /** @@ -347,12 +347,13 @@ public class DataArtifactContentViewer extends javax.swing.JPanel implements Dat for (Content content : node.getLookup().lookupAll(Content.class)) { if ((content != null) && (!(content instanceof DataArtifact)) && (!(content instanceof AnalysisResult))) { try { - return content.hasDataArtifacts(); - } catch (TskException ex) { + return Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasDataArtifacts(content.getId()); + } catch (NoCurrentCaseException | TskException ex) { logger.log(Level.SEVERE, "Couldn't get count of DataArtifacts for content", ex); //NON-NLS } } } + return false; } @@ -382,7 +383,15 @@ public class DataArtifactContentViewer extends javax.swing.JPanel implements Dat return LESS_PREFERRED; } - return MORE_PREFERRED; + switch (artifactType.getCategory()) { + // data artifacts should be more preferred + case DATA_ARTIFACT: + return MORE_PREFERRED; + // everything else is less preferred + case ANALYSIS_RESULT: + default: + return LESS_PREFERRED; + } } private ArtifactContentViewer getSupportingViewer(DataArtifact artifact) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 822ff78be4..742d656a82 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -486,10 +486,5 @@ public final class FileTypes implements AutopsyVisitableItem { public List getAllDataArtifacts() throws TskCoreException { return content.getAllDataArtifacts(); } - - @Override - public boolean hasDataArtifacts() throws TskCoreException { - return content.hasDataArtifacts(); - } } } From 5a4f3b1118cf35957fd9a23b9022eeb88c23fa53 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 15:33:16 -0400 Subject: [PATCH 58/78] reset numbers for ordering and worked through initial prototype --- .../autopsy/contentviewers/Metadata.java | 2 +- .../AnalysisResultsContentViewer.form | 12 ++--- .../AnalysisResultsContentViewer.java | 51 ++++++++++--------- .../osaccount/OsAccountViewer.java | 2 +- .../DataArtifactContentViewer.java | 2 +- 5 files changed, 34 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 2c695dfcb4..ea51d54d4e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; * shows the same data that can also be found in the ResultViewer table, just a * different order and allows the full path to be visible in the bottom area. */ -@ServiceProvider(service = DataContentViewer.class, position = 6) +@ServiceProvider(service = DataContentViewer.class, position = 4) @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class Metadata extends javax.swing.JPanel implements DataContentViewer { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form index fb2b7c3d9b..bbf986fcfd 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form @@ -8,9 +8,6 @@ - - - @@ -45,9 +42,6 @@ - - - @@ -58,10 +52,10 @@ - - + + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index aced0974c6..25ae248d1d 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -42,6 +42,9 @@ import org.jsoup.nodes.Element; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.contentviewers.application.Annotations; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; @@ -53,6 +56,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Displays a list of analysis results as a content viewer. */ +@ServiceProvider(service = DataContentViewer.class, position = 7) public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); @@ -239,23 +243,6 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements return new NodeAnalysisResults(allAnalysisResults.values(), selectedResult); } - private static void refresh(Node node) { - NodeAnalysisResults nodeResults = getAnalysisResults(node); - List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); - List displayAttributes = getDisplayAttributes(orderedAnalysisResults); - - Optional aggregateScore = displayAttributes.stream() - .findFirst() - .flatMap(dispAttrs -> Optional.ofNullable(dispAttrs.getAnalysisResult().getScore())); - - render(displayAttributes, aggregateScore); - - Optional selectedResult = nodeResults.getSelectedResult(); - if (selectedResult.isPresent()) { - // GVDTODO - } - } - private static Document render(List displayAttributes, Optional aggregateScore) { Document html = Jsoup.parse(EMPTY_HTML); Element body = html.getElementsByTag("body").first(); @@ -358,12 +345,32 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Override public void resetComponent() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + textPane.setText(""); } @Override public void setNode(Node selectedNode) { + // GVDTODO comment, put in swing worker, scroll to location + NodeAnalysisResults nodeResults = getAnalysisResults(selectedNode); + List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); + List displayAttributes = getDisplayAttributes(orderedAnalysisResults); + + Optional aggregateScore = displayAttributes.stream() + .findFirst() + .flatMap(dispAttrs -> Optional.ofNullable(dispAttrs.getAnalysisResult().getScore())); + + Document document = render(displayAttributes, aggregateScore); + + Optional selectedResult = nodeResults.getSelectedResult(); + if (selectedResult.isPresent()) { + // GVDTODO + } + + textPane.setText(document.html()); + textPane.setCaretPosition(0); + this.selectedNode = selectedNode; + } @Override @@ -375,10 +382,10 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); if (abstractFile != null) { try { - if (abstractFile.hasAnalysisResults()) { + if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(abstractFile.getId())) { return true; } - } catch (TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); } } @@ -406,14 +413,12 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements setMaximumSize(null); setMinimumSize(new java.awt.Dimension(250, 250)); - setPreferredSize(null); scrollPane.setMaximumSize(null); scrollPane.setMinimumSize(null); - scrollPane.setPreferredSize(null); + textPane.setBackground(DEFAULT_BACKGROUND); textPane.setMaximumSize(null); - textPane.setMinimumSize(null); textPane.setPreferredSize(null); scrollPane.setViewportView(textPane); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java index 2195e4fbdb..4b2b4ac972 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountViewer.java @@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * DataContentViewer for OsAccounts. */ -@ServiceProvider(service = DataContentViewer.class, position = 7) +@ServiceProvider(service = DataContentViewer.class, position = 5) public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java index 662df44a22..596a97d44f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataArtifactContentViewer.java @@ -53,7 +53,7 @@ import org.sleuthkit.datamodel.DataArtifact; * It goes through a list of known ArtifactContentViewer to find a viewer that * supports a given artifact and then hands it the artifact to display. */ -@ServiceProvider(service = DataContentViewer.class, position = 7) +@ServiceProvider(service = DataContentViewer.class, position = 6) @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataArtifactContentViewer extends javax.swing.JPanel implements DataContentViewer { From 0035c47202305d1f009d0fe59b3a1b904c1a07ff Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 May 2021 15:33:22 -0400 Subject: [PATCH 59/78] reset numbers for ordering and worked through initial prototype --- .../analysisresults/Bundle.properties-MERGED | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED new file mode 100644 index 0000000000..b73e017d67 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED @@ -0,0 +1,9 @@ +AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score +# {0} - analysisResultsNumber +AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0} +AnalysisResultsContentViewer_displayAttributes_conclusion=Conclusion +AnalysisResultsContentViewer_displayAttributes_configuration=Configuration +AnalysisResultsContentViewer_displayAttributes_score=Score +AnalysisResultsContentViewer_displayAttributes_type=Type +AnalysisResultsContentViewer_title=Analysis Results +AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node. From 55954dfcd4e4acb6bbc9cbe1784f90e1ae30b109 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 26 May 2021 15:47:49 -0400 Subject: [PATCH 60/78] Fixed NPE that will found when integrating all of the content viewer changes --- .../sleuthkit/autopsy/corecomponents/DataContentPanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index ae49ab720a..8e8183e2f1 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -140,6 +140,8 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, workerThread = null; } + currentNode = null; + // Reset everything for (int index = 0; index < jTabbedPane1.getTabCount(); index++) { jTabbedPane1.setEnabledAt(index, false); @@ -299,7 +301,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, try { WorkerResults results = get(); - + currentNode = node; if (results != null) { updateTabs(results.getNode(), results.getSupportedIndices(), results.getPreferredViewerIndex()); } From 543fdd369b1e79d470c968d7303a3575e5df2161 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 27 May 2021 08:37:29 -0400 Subject: [PATCH 61/78] Update VMExtractorIngestModule.java Moved defining fileTypeDetector outside of loop so it only happens once. If there is an error initializing fileTypeDetector then add the file to the list of VM's that we look at even if it may not be a VM, this way we do not loose it. Continue on in the loop skipping over a potential NPE in the code after the initilization. --- .../autopsy/modules/vmextractor/VMExtractorIngestModule.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java index a79b0f1a12..80219c8e9d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java @@ -250,16 +250,17 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { */ private static List removeNonVMFiles(List vmFiles) { List vFile = new ArrayList<>(); - + FileTypeDetector fileTypeDetector = null; for (AbstractFile vmFile : vmFiles) { if (vmFile.getNameExtension().equalsIgnoreCase("vhd")) { String fileMimeType = vmFile.getMIMEType(); if (fileMimeType == null) { - FileTypeDetector fileTypeDetector = null; try { fileTypeDetector = new FileTypeDetector(); } catch (FileTypeDetector.FileTypeDetectorInitException ex) { logger.log(Level.WARNING, String.format("Unable to create file type detector for determining MIME type for file %s with id of %d", vmFile.getName(), vmFile.getId())); + vFile.add(vmFile); + continue; } fileMimeType = fileTypeDetector.getMIMEType(vmFile); try { From 4c8d1f50cce5e94f00d407a9b9de0fad1d3f5a0a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 08:57:14 -0400 Subject: [PATCH 62/78] cleanup --- .../AnalysisResultsContentViewer.form | 40 ++++----- .../AnalysisResultsContentViewer.java | 90 +++++++------------ 2 files changed, 46 insertions(+), 84 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form index bbf986fcfd..b7bfe88963 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form @@ -2,12 +2,12 @@
- - - + + + @@ -21,45 +21,35 @@ - - - - - - - - - - - - + - - - - - + + + + + + + - + + - - - + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index 25ae248d1d..bb79e1fb67 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -23,7 +23,6 @@ import java.awt.Component; import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,7 +45,6 @@ import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.contentviewers.Utilities; -import org.sleuthkit.autopsy.contentviewers.application.Annotations; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; @@ -60,7 +58,12 @@ import org.sleuthkit.datamodel.TskCoreException; public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); + + /** + * isPreferred value. + */ private static final int PREFERRED_VALUE = 6; + private static final String EMPTY_HTML = ""; private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); @@ -68,46 +71,28 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground(); // html stylesheet classnames for components - public static final String MESSAGE_CLASSNAME = "message"; - public static final String SUBSECTION_CLASSNAME = "subsection"; - public static final String SUBHEADER_CLASSNAME = "subheader"; public static final String SECTION_CLASSNAME = "section"; public static final String HEADER_CLASSNAME = "header"; - // how big the subheader should be - private static final int SUBHEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 12 / 11; // how big the header should be - private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 14 / 11; + private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2; - // the subsection indent - private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE; // spacing occurring after an item - private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE * 2; - private static final int DEFAULT_SUBSECTION_SPACING = DEFAULT_FONT_SIZE / 2; - private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; + private static final int DEFAULT_SECTION_SPACING = 6; + private static final int CELL_SPACING = 4; // additional styling for components - private static final String STYLE_SHEET_RULE - = String.format(" .%s { font-family: %s; font-size: %dpt; font-style:italic; margin: 0px; padding: 0px; } ", - Annotations.MESSAGE_CLASSNAME, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE) - + String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; margin-top: %dpx; padding: 0px; } ", - Annotations.SUBHEADER_CLASSNAME, DEFAULT_FONT_FAMILY, SUBHEADER_FONT_SIZE, DEFAULT_SUBSECTION_SPACING) - + String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; padding: 0px; } ", - Annotations.HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + private static final String STYLE_SHEET_RULE = + String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; padding: 0px; } ", + HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + String.format(" td { vertical-align: top; font-family: %s; font-size:%dpt; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px;} ", DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) + String.format(" th { vertical-align: top; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px} ", DEFAULT_FONT_SIZE, CELL_SPACING) - + String.format(" .%s { margin: %dpx 0px; padding-left: %dpx; } ", Annotations.SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_SPACING, DEFAULT_SUBSECTION_LEFT_PAD) - + String.format(" .%s { margin-bottom: %dpx; } ", Annotations.SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); + + String.format(" .%s { margin-bottom: %dpx; } ", SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); - private static Optional getAggregateScore(Collection analysisResults) { - return analysisResults.stream() - .map(AnalysisResult::getScore) - .max(Comparator.naturalOrder()); - } private static String normalizeAttr(String originalAttrStr) { return (originalAttrStr == null) ? "" : originalAttrStr.trim(); @@ -151,7 +136,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_score(), normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_type(), - normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), + normalizeAttr(type)), Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_configuration(), normalizeAttr(analysisResult.getConfiguration())), Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_conclusion(), @@ -270,9 +255,8 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements "AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0}" }) private static void appendResult(Element parent, int index, ResultDisplayAttributes attrs) { - Element sectionElement = appendSection(parent, Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1)); - - Element table = parent.appendElement("table"); + Element sectionDiv = appendSection(parent, Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1)); + Element table = sectionDiv.appendElement("table"); Element tableBody = table.appendElement("tbody"); for (Pair keyVal : attrs.getAttributesToDisplay()) { @@ -301,16 +285,15 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements return sectionDiv; } - private Node selectedNode; /** * Creates new form AnalysisResultsContentViewer */ public AnalysisResultsContentViewer() { initComponents(); - Utilities.configureTextPaneAsHtml(textPane); + Utilities.configureTextPaneAsHtml(textPanel); // get html editor kit and apply additional style rules - EditorKit editorKit = textPane.getEditorKit(); + EditorKit editorKit = textPanel.getEditorKit(); if (editorKit instanceof HTMLEditorKit) { HTMLEditorKit htmlKit = (HTMLEditorKit) editorKit; htmlKit.getStyleSheet().addRule(STYLE_SHEET_RULE); @@ -345,7 +328,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Override public void resetComponent() { - textPane.setText(""); + textPanel.setText(""); } @Override @@ -366,11 +349,8 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // GVDTODO } - textPane.setText(document.html()); - textPane.setCaretPosition(0); - - this.selectedNode = selectedNode; - + textPanel.setText(document.html()); + textPanel.setCaretPosition(0); } @Override @@ -409,33 +389,25 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private void initComponents() { javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); - textPane = new javax.swing.JTextPane(); + textPanel = new javax.swing.JTextPane(); - setMaximumSize(null); setMinimumSize(new java.awt.Dimension(250, 250)); + setPreferredSize(new java.awt.Dimension(250, 250)); + setLayout(new java.awt.BorderLayout()); - scrollPane.setMaximumSize(null); - scrollPane.setMinimumSize(null); + scrollPane.setPreferredSize(new java.awt.Dimension(32767, 32767)); - textPane.setBackground(DEFAULT_BACKGROUND); - textPane.setMaximumSize(null); - textPane.setPreferredSize(null); - scrollPane.setViewportView(textPane); + textPanel.setEditable(false); + textPanel.setBackground(DEFAULT_BACKGROUND); + textPanel.setName(""); // NOI18N + textPanel.setPreferredSize(new java.awt.Dimension(600, 52)); + scrollPane.setViewportView(textPanel); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - ); + add(scrollPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JTextPane textPane; + private javax.swing.JTextPane textPanel; // End of variables declaration//GEN-END:variables } From b24edcc27731f4d807afc72448d0ccd8c7440511 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 10:14:22 -0400 Subject: [PATCH 63/78] formatting --- .../AnalysisResultsContentViewer.form | 32 ++++++------ .../AnalysisResultsContentViewer.java | 49 +++++++++---------- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form index b7bfe88963..22fbe9e8a1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form @@ -1,19 +1,16 @@ - + - - - - + - + @@ -21,23 +18,24 @@ - + + + + + + + + + + + + - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index bb79e1fb67..62fb5b49ea 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -32,7 +32,6 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.JLabel; -import javax.swing.text.EditorKit; import javax.swing.text.html.HTMLEditorKit; import org.apache.commons.lang3.tuple.Pair; import org.jsoup.Jsoup; @@ -44,7 +43,6 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; @@ -71,8 +69,8 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground(); // html stylesheet classnames for components - public static final String SECTION_CLASSNAME = "section"; - public static final String HEADER_CLASSNAME = "header"; + private static final String SPACED_SECTION_CLASSNAME = "spacedSection"; + private static final String HEADER_CLASSNAME = "header"; // how big the header should be @@ -80,18 +78,16 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // spacing occurring after an item - private static final int DEFAULT_SECTION_SPACING = 6; - private static final int CELL_SPACING = 4; + private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; + private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; // additional styling for components private static final String STYLE_SHEET_RULE = - String.format(" .%s { font-family: %s; font-size:%dpt;font-weight:bold; margin: 0px; padding: 0px; } ", + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) - + String.format(" td { vertical-align: top; font-family: %s; font-size:%dpt; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px;} ", + + String.format(" td { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) - + String.format(" th { vertical-align: top; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px} ", - DEFAULT_FONT_SIZE, CELL_SPACING) - + String.format(" .%s { margin-bottom: %dpx; } ", SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); + + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); private static String normalizeAttr(String originalAttrStr) { @@ -278,7 +274,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements */ private static Element appendSection(Element parent, String headerText) { Element sectionDiv = parent.appendElement("div"); - sectionDiv.attr("class", SECTION_CLASSNAME); + sectionDiv.attr("class", SPACED_SECTION_CLASSNAME); Element header = sectionDiv.appendElement("h1"); header.text(headerText); header.attr("class", HEADER_CLASSNAME); @@ -291,13 +287,11 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements */ public AnalysisResultsContentViewer() { initComponents(); - Utilities.configureTextPaneAsHtml(textPanel); - // get html editor kit and apply additional style rules - EditorKit editorKit = textPanel.getEditorKit(); - if (editorKit instanceof HTMLEditorKit) { - HTMLEditorKit htmlKit = (HTMLEditorKit) editorKit; - htmlKit.getStyleSheet().addRule(STYLE_SHEET_RULE); - } + + textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS + HTMLEditorKit kit = new HTMLEditorKit(); + textPanel.setEditorKit(kit); + kit.getStyleSheet().addRule(STYLE_SHEET_RULE); } @NbBundle.Messages({ @@ -391,11 +385,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); textPanel = new javax.swing.JTextPane(); - setMinimumSize(new java.awt.Dimension(250, 250)); - setPreferredSize(new java.awt.Dimension(250, 250)); - setLayout(new java.awt.BorderLayout()); - - scrollPane.setPreferredSize(new java.awt.Dimension(32767, 32767)); + setPreferredSize(new java.awt.Dimension(100, 58)); textPanel.setEditable(false); textPanel.setBackground(DEFAULT_BACKGROUND); @@ -403,7 +393,16 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements textPanel.setPreferredSize(new java.awt.Dimension(600, 52)); scrollPane.setViewportView(textPanel); - add(scrollPane, java.awt.BorderLayout.CENTER); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE) + ); }// //GEN-END:initComponents From f4d521cb7ce58473b193eb17bb8be40db35febf5 Mon Sep 17 00:00:00 2001 From: apriestman Date: Thu, 27 May 2021 10:58:31 -0400 Subject: [PATCH 64/78] Display path string and path bytes when an error occurs --- .../SevenZipExtractor.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 6fdb53a5c3..da046d729b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -1309,13 +1309,29 @@ class SevenZipExtractor { } if (tokens.size() != byteTokens.size()) { - logger.log(Level.WARNING, "Could not map path bytes to path string"); + logger.log(Level.WARNING, "Could not map path bytes to path string (path string: \"{0}\", bytes: {1})", + new Object[]{filePath, bytesToString(filePathBytes)}); return addNode(rootNode, tokens, null); } } return addNode(rootNode, tokens, byteTokens); } + + /** + * Convert byte array to string representation. + * + * @param bytes Byte array + * + * @return Byte array as lower case hex string. + */ + private String bytesToString(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); + } /** * recursive method that traverses the path From cd9c60d87f16af9e30a0a93e329e4f0ba555c377 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 11:44:47 -0400 Subject: [PATCH 65/78] updates --- .../contentviewers/Bundle.properties-MERGED | 3 - .../AnalysisResultsContentViewer.java | 140 +++++++++++++----- 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 4714416d46..781b350e23 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -15,9 +15,6 @@ # governing permissions and limitations under the License. # -AnnotationsContentViewer.onEmpty=No annotations were found for this particular item. -AnnotationsContentViewer.title=Annotations -AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index 62fb5b49ea..d7d1f43423 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -32,6 +32,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.JLabel; +import javax.swing.SwingWorker; import javax.swing.text.html.HTMLEditorKit; import org.apache.commons.lang3.tuple.Pair; import org.jsoup.Jsoup; @@ -44,8 +45,12 @@ import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; @@ -56,12 +61,12 @@ import org.sleuthkit.datamodel.TskCoreException; public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); - + /** * isPreferred value. */ private static final int PREFERRED_VALUE = 6; - + private static final String EMPTY_HTML = ""; private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); @@ -71,25 +76,26 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // html stylesheet classnames for components private static final String SPACED_SECTION_CLASSNAME = "spacedSection"; private static final String HEADER_CLASSNAME = "header"; + public static final String MESSAGE_CLASSNAME = "message"; + private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_"; // how big the header should be private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2; - // spacing occurring after an item private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; // additional styling for components - private static final String STYLE_SHEET_RULE = - String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", + private static final String STYLE_SHEET_RULE + = String.format(" .%s { font-size: %dpx;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) + + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + String.format(" td { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); - private static String normalizeAttr(String originalAttrStr) { return (originalAttrStr == null) ? "" : originalAttrStr.trim(); } @@ -174,37 +180,51 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements private final Collection analysisResults; private final Optional selectedResult; + private final Optional aggregateScore; - NodeAnalysisResults(Collection analysisResults, Optional selectedResult) { + public NodeAnalysisResults(Collection analysisResults, Optional selectedResult, Optional aggregateScore) { this.analysisResults = analysisResults; this.selectedResult = selectedResult; + this.aggregateScore = aggregateScore; } - Collection getAnalysisResults() { + public Collection getAnalysisResults() { return analysisResults; } - Optional getSelectedResult() { + public Optional getSelectedResult() { return selectedResult; } + + public Optional getAggregateScore() { + return aggregateScore; + } } private static NodeAnalysisResults getAnalysisResults(Node node) { if (node == null) { - return new NodeAnalysisResults(Collections.emptyList(), Optional.empty()); + return new NodeAnalysisResults(Collections.emptyList(), Optional.empty(), Optional.empty()); } + Optional aggregateScore = Optional.empty(); Map allAnalysisResults = new HashMap<>(); Optional selectedResult = Optional.empty(); - AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); - if (abstractFile != null) { + for (Content content : node.getLookup().lookupAll(Content.class)) { + if (content == null || content instanceof BlackboardArtifact) { + continue; + } + try { - abstractFile.getAllAnalysisResults().stream() + aggregateScore = Optional.ofNullable(content.getAggregateScore()); + + content.getAllAnalysisResults().stream() .filter(ar -> ar != null) .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + + break; } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); + logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex); } } @@ -219,9 +239,13 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements selectedResult = filteredResults.stream() .max((a, b) -> a.getScore().compareTo(b.getScore())); + + if (!aggregateScore.isPresent()) { + aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore())); + } } - return new NodeAnalysisResults(allAnalysisResults.values(), selectedResult); + return new NodeAnalysisResults(getScoreOrderedResults(allAnalysisResults.values()), selectedResult, aggregateScore); } private static Document render(List displayAttributes, Optional aggregateScore) { @@ -242,16 +266,24 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Messages("AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score") private static void appendAggregateScore(Element body, Score score) { - appendSection(body, MessageFormat.format("{0}: {1}", - Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), - score.getSignificance().getDisplayName())); + appendSection(body, + MessageFormat.format("{0}: {1}", + Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), + score.getSignificance().getDisplayName()), + null); + } + + private static String getAnchor(AnalysisResult analysisResult) { + return RESULT_ANCHOR_PREFIX + analysisResult.getId(); } @Messages({"# {0} - analysisResultsNumber", "AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0}" }) private static void appendResult(Element parent, int index, ResultDisplayAttributes attrs) { - Element sectionDiv = appendSection(parent, Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1)); + Element sectionDiv = appendSection(parent, + Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1), + Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); Element table = sectionDiv.appendElement("table"); Element tableBody = table.appendElement("tbody"); @@ -269,11 +301,17 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements * * @param parent The element to append this section to. * @param headerText The text for the section. + * @param anchorId The anchor id for this section. * * @return The div for the new section. */ - private static Element appendSection(Element parent, String headerText) { + private static Element appendSection(Element parent, String headerText, Optional anchorId) { Element sectionDiv = parent.appendElement("div"); + if (anchorId.isPresent()) { + Element anchorEl = sectionDiv.appendElement("a"); + anchorEl.attr("name", anchorId.get()); + } + sectionDiv.attr("class", SPACED_SECTION_CLASSNAME); Element header = sectionDiv.appendElement("h1"); header.text(headerText); @@ -281,13 +319,14 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements return sectionDiv; } + private SwingWorker worker = null; /** * Creates new form AnalysisResultsContentViewer */ public AnalysisResultsContentViewer() { initComponents(); - + textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS HTMLEditorKit kit = new HTMLEditorKit(); textPanel.setEditorKit(kit); @@ -326,25 +365,52 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements } @Override - public void setNode(Node selectedNode) { - // GVDTODO comment, put in swing worker, scroll to location - NodeAnalysisResults nodeResults = getAnalysisResults(selectedNode); - List orderedAnalysisResults = getScoreOrderedResults(nodeResults.getAnalysisResults()); - List displayAttributes = getDisplayAttributes(orderedAnalysisResults); + @Messages({ + "AnalysisResultsContentViewer_setNode_loadingMessage=Loading...", + "AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results.",}) + public synchronized void setNode(Node node) { + resetComponent(); - Optional aggregateScore = displayAttributes.stream() - .findFirst() - .flatMap(dispAttrs -> Optional.ofNullable(dispAttrs.getAnalysisResult().getScore())); - - Document document = render(displayAttributes, aggregateScore); - - Optional selectedResult = nodeResults.getSelectedResult(); - if (selectedResult.isPresent()) { - // GVDTODO + if (worker != null) { + worker.cancel(true); + worker = null; } - + + if (node == null) { + return; + } + + showMessage(Bundle.AnalysisResultsContentViewer_setNode_loadingMessage()); + + worker = new DataFetchWorker( + (selectedNode) -> getAnalysisResults(selectedNode), + (nodeAnalysisResults) -> { + if (nodeAnalysisResults.getResultType() == ResultType.SUCCESS) { + displayResults(nodeAnalysisResults.getData()); + } else { + showMessage(Bundle.AnalysisResultsContentViewer_setNode_errorMessage()); + } + }, + node); + + worker.execute(); + } + + private void showMessage(String message) { + textPanel.setText("" + + MessageFormat.format("

{1}

", MESSAGE_CLASSNAME, message) + + ""); + } + + private void displayResults(NodeAnalysisResults nodeResults) { + List displayAttributes = getDisplayAttributes(nodeResults.getAnalysisResults()); + Document document = render(displayAttributes, nodeResults.getAggregateScore()); + Optional selectedResult = nodeResults.getSelectedResult(); textPanel.setText(document.html()); - textPanel.setCaretPosition(0); + + if (selectedResult.isPresent()) { + textPanel.scrollToReference(getAnchor(selectedResult.get())); + } } @Override From 6a7a8c9ba6d20449ee4d9e14c3214ffa43d809e9 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 12:11:47 -0400 Subject: [PATCH 66/78] indent --- .../AnalysisResultsContentViewer.java | 13 ++++++++++--- .../analysisresults/Bundle.properties-MERGED | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index d7d1f43423..82f014fde8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -75,6 +75,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // html stylesheet classnames for components private static final String SPACED_SECTION_CLASSNAME = "spacedSection"; + private static final String SUBSECTION_CLASSNAME = "subsection"; private static final String HEADER_CLASSNAME = "header"; public static final String MESSAGE_CLASSNAME = "message"; @@ -86,15 +87,19 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements // spacing occurring after an item private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; + + // the subsection indent + private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE; // additional styling for components private static final String STYLE_SHEET_RULE - = String.format(" .%s { font-size: %dpx;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) + = String.format(" .%s { font-size: %dpt;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + String.format(" td { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) - + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING); + + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING) + + String.format(" .%s { padding-left: %dpt; }", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_LEFT_PAD); private static String normalizeAttr(String originalAttrStr) { return (originalAttrStr == null) ? "" : originalAttrStr.trim(); @@ -270,7 +275,7 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements MessageFormat.format("{0}: {1}", Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), score.getSignificance().getDisplayName()), - null); + Optional.empty()); } private static String getAnchor(AnalysisResult analysisResult) { @@ -285,6 +290,8 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1), Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); Element table = sectionDiv.appendElement("table"); + table.attr("class", SUBSECTION_CLASSNAME); + Element tableBody = table.appendElement("tbody"); for (Pair keyVal : attrs.getAttributesToDisplay()) { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED index b73e017d67..c3ca847cc4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED @@ -5,5 +5,7 @@ AnalysisResultsContentViewer_displayAttributes_conclusion=Conclusion AnalysisResultsContentViewer_displayAttributes_configuration=Configuration AnalysisResultsContentViewer_displayAttributes_score=Score AnalysisResultsContentViewer_displayAttributes_type=Type +AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results. +AnalysisResultsContentViewer_setNode_loadingMessage=Loading... AnalysisResultsContentViewer_title=Analysis Results AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node. From b292c6e434e9df5ec8b1a29635f7437bdf26681b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 27 May 2021 14:44:33 -0400 Subject: [PATCH 67/78] 7633 change display name of accounts to communication accounts --- .../org/sleuthkit/autopsy/datamodel/accounts/Accounts.java | 4 ++-- .../autopsy/datamodel/accounts/Bundle.properties-MERGED | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index 18136db99e..71b451afc6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -97,7 +97,7 @@ final public class Accounts implements AutopsyVisitableItem { private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName(); - @NbBundle.Messages("AccountsRootNode.name=Accounts") + @NbBundle.Messages("AccountsRootNode.name=Accounts") //used for the viewArtifact navigation final public static String NAME = Bundle.AccountsRootNode_name(); private SleuthkitCase skCase; @@ -233,7 +233,7 @@ final public class Accounts implements AutopsyVisitableItem { /** * Top-level node for the accounts tree */ - @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"}) + @NbBundle.Messages({"Accounts.RootNode.displayName=Communication Accounts"}) final public class AccountsRootNode extends UpdatableCountTypeNode { public AccountsRootNode() { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties-MERGED index 3f5cc586e2..f7211362ec 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties-MERGED @@ -21,7 +21,7 @@ Accounts.FileWithCCNNode.statusProperty.displayName=Status # {0} - raw file name # {1} - solr chunk id Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1} -Accounts.RootNode.displayName=Accounts +Accounts.RootNode.displayName=Communication Accounts AccountsRootNode.name=Accounts ApproveAccountsAction.name=Approve Accounts RejectAccountsAction.name=Reject Accounts From 504e25ec308a2e4d9ac65e97bf577f8fa0a8774f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 15:13:42 -0400 Subject: [PATCH 68/78] refactor --- .../contentviewers/Bundle.properties-MERGED | 3 + ....form => AnalysisResultsContentPanel.form} | 0 .../AnalysisResultsContentPanel.java | 266 ++++++++++++ .../AnalysisResultsContentViewer.java | 410 ++---------------- .../analysisresults/Bundle.properties-MERGED | 12 +- 5 files changed, 317 insertions(+), 374 deletions(-) rename Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/{AnalysisResultsContentViewer.form => AnalysisResultsContentPanel.form} (100%) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 781b350e23..4714416d46 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -15,6 +15,9 @@ # governing permissions and limitations under the License. # +AnnotationsContentViewer.onEmpty=No annotations were found for this particular item. +AnnotationsContentViewer.title=Annotations +AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.form similarity index 100% rename from Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.form rename to Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.form diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.java new file mode 100644 index 0000000000..72ab3c6853 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentPanel.java @@ -0,0 +1,266 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.contentviewers.analysisresults; + +import java.awt.Color; +import java.text.MessageFormat; +import java.util.List; +import java.util.Optional; +import javax.swing.JLabel; +import javax.swing.text.html.HTMLEditorKit; +import org.apache.commons.lang3.tuple.Pair; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.NodeResults; +import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.ResultDisplayAttributes; +import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.Score; + +/** + * Displays a list of analysis results in a panel. + */ +public class AnalysisResultsContentPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + private static final String EMPTY_HTML = ""; + + private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); + private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize(); + private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground(); + + // html stylesheet classnames for components + private static final String ANALYSIS_RESULTS_CLASS_PREFIX = "analysisResult_"; + private static final String SPACED_SECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "spacedSection"; + private static final String SUBSECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "subsection"; + private static final String HEADER_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "header"; + public static final String MESSAGE_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "message"; + public static final String TD_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "td"; + + // Anchors are inserted into the navigation so that the viewer can navigate to a selection. + // This is the prefix of those anchors. + private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_"; + + // how big the header should be + private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2; + + // spacing occurring after an item + private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; + private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; + + // the subsection indent + private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE; + + // additional styling for components + private static final String STYLE_SHEET_RULE + = String.format(" .%s { font-size: %dpt;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) + + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", + HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) + + String.format(" .%s { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", + TD_CLASSNAME, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) + + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING) + + String.format(" .%s { padding-left: %dpt; }", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_LEFT_PAD); + + + + /** + * Creates new form AnalysisResultsContentViewer + */ + public AnalysisResultsContentPanel() { + initComponents(); + + textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS + HTMLEditorKit kit = new HTMLEditorKit(); + textPanel.setEditorKit(kit); + kit.getStyleSheet().addRule(STYLE_SHEET_RULE); + } + + /** + * Clears current text and shows text provided in the message. + * + * @param message The message to be displayed. + */ + void showMessage(String message) { + textPanel.setText("" + + MessageFormat.format("

{1}

", + MESSAGE_CLASSNAME, + message == null ? "" : message) + + ""); + } + + /** + * Resets the current view and displays nothing. + */ + void reset() { + textPanel.setText(EMPTY_HTML); + } + + /** + * Displays analysis results for the node in the text pane. + * + * @param nodeResults The analysis results data to display. + */ + @NbBundle.Messages("AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score") + void displayResults(NodeResults nodeResults) { + Document document = Jsoup.parse(EMPTY_HTML); + Element body = document.getElementsByTag("body").first(); + + // if there is an aggregate score, append a section with the value + Optional aggregateScore = nodeResults.getAggregateScore(); + if (aggregateScore.isPresent()) { + appendSection(body, + MessageFormat.format("{0}: {1}", + Bundle.AnalysisResultsContentPanel_aggregateScore_displayKey(), + aggregateScore.get().getSignificance().getDisplayName()), + Optional.empty()); + } + + // for each analysis result item, display the data. + List displayAttributes = nodeResults.getAnalysisResults(); + for (int idx = 0; idx < displayAttributes.size(); idx++) { + AnalysisResultsViewModel.ResultDisplayAttributes resultAttrs = displayAttributes.get(idx); + appendResult(body, idx, resultAttrs); + } + + // set the body html + textPanel.setText(document.html()); + + // if there is a selected result scroll to it + Optional selectedResult = nodeResults.getSelectedResult(); + if (selectedResult.isPresent()) { + textPanel.scrollToReference(getAnchor(selectedResult.get())); + } + } + + /** + * Returns the anchor id to use with the analysis result (based on the id). + * @param analysisResult The analysis result. + * @return The anchor id. + */ + private String getAnchor(AnalysisResult analysisResult) { + return RESULT_ANCHOR_PREFIX + analysisResult.getId(); + } + + + /** + * Appends a result item to the parent element of an html document. + * @param parent The parent element. + * @param index The index of the item in the list of all items. + * @param attrs The attributes of this item. + */ + @NbBundle.Messages({"# {0} - analysisResultsNumber", + "AnalysisResultsContentPanel_result_headerKey=Analysis Result {0}" + }) + private void appendResult(Element parent, int index, AnalysisResultsViewModel.ResultDisplayAttributes attrs) { + // create a new section with appropriate header + Element sectionDiv = appendSection(parent, + Bundle.AnalysisResultsContentPanel_result_headerKey(index + 1), + Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); + + // create a table + Element table = sectionDiv.appendElement("table"); + table.attr("class", SUBSECTION_CLASSNAME); + + Element tableBody = table.appendElement("tbody"); + + // append a row for each item + for (Pair keyVal : attrs.getAttributesToDisplay()) { + Element row = tableBody.appendElement("tr"); + String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":"; + row.appendElement("td") + .text(keyString) + .attr("class", TD_CLASSNAME); + + String valueString = keyVal.getValue() == null ? "" : keyVal.getValue(); + row.appendElement("td") + .text(valueString) + .attr("class", TD_CLASSNAME); + } + } + + /** + * Appends a new section with a section header to the parent element. + * + * @param parent The element to append this section to. + * @param headerText The text for the section. + * @param anchorId The anchor id for this section. + * + * @return The div for the new section. + */ + private Element appendSection(Element parent, String headerText, Optional anchorId) { + Element sectionDiv = parent.appendElement("div"); + + // append an anchor tag if there is one + if (anchorId.isPresent()) { + Element anchorEl = sectionDiv.appendElement("a"); + anchorEl.attr("name", anchorId.get()); + } + + // set the class for the section + sectionDiv.attr("class", SPACED_SECTION_CLASSNAME); + + // append the header + Element header = sectionDiv.appendElement("h1"); + header.text(headerText); + header.attr("class", HEADER_CLASSNAME); + + // return the section element + return sectionDiv; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); + textPanel = new javax.swing.JTextPane(); + + setPreferredSize(new java.awt.Dimension(100, 58)); + + textPanel.setEditable(false); + textPanel.setBackground(DEFAULT_BACKGROUND); + textPanel.setName(""); // NOI18N + textPanel.setPreferredSize(new java.awt.Dimension(600, 52)); + scrollPane.setViewportView(textPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextPane textPanel; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index 82f014fde8..f853eae777 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -18,327 +18,39 @@ */ package org.sleuthkit.autopsy.contentviewers.analysisresults; -import java.awt.Color; import java.awt.Component; -import java.text.MessageFormat; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.swing.JLabel; import javax.swing.SwingWorker; -import javax.swing.text.html.HTMLEditorKit; -import org.apache.commons.lang3.tuple.Pair; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; import org.openide.nodes.Node; import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; /** * Displays a list of analysis results as a content viewer. */ @ServiceProvider(service = DataContentViewer.class, position = 7) -public class AnalysisResultsContentViewer extends javax.swing.JPanel implements DataContentViewer { - - private static Logger logger = Logger.getLogger(AnalysisResultsContentViewer.class.getName()); - - /** - * isPreferred value. - */ - private static final int PREFERRED_VALUE = 6; - - private static final String EMPTY_HTML = ""; - - private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily(); - private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize(); - private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground(); - - // html stylesheet classnames for components - private static final String SPACED_SECTION_CLASSNAME = "spacedSection"; - private static final String SUBSECTION_CLASSNAME = "subsection"; - private static final String HEADER_CLASSNAME = "header"; - public static final String MESSAGE_CLASSNAME = "message"; - - private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_"; - - // how big the header should be - private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2; - - // spacing occurring after an item - private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2; - private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2; +public class AnalysisResultsContentViewer implements DataContentViewer { + private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName()); + + // isPreferred value + private static final int PREFERRED_VALUE = 6; + + private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel(); + private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel(); - // the subsection indent - private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE; - - // additional styling for components - private static final String STYLE_SHEET_RULE - = String.format(" .%s { font-size: %dpt;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE) - + String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ", - HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE) - + String.format(" td { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ", - DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING) - + String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING) - + String.format(" .%s { padding-left: %dpt; }", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_LEFT_PAD); - - private static String normalizeAttr(String originalAttrStr) { - return (originalAttrStr == null) ? "" : originalAttrStr.trim(); - } - - private static class ResultDisplayAttributes { - - private final AnalysisResult analysisResult; - private final List> attributesToDisplay; - - ResultDisplayAttributes(AnalysisResult analysisResult, List> attributesToDisplay) { - this.analysisResult = analysisResult; - this.attributesToDisplay = attributesToDisplay; - } - - List> getAttributesToDisplay() { - return attributesToDisplay; - } - - AnalysisResult getAnalysisResult() { - return analysisResult; - } - } - - @Messages({ - "AnalysisResultsContentViewer_displayAttributes_score=Score", - "AnalysisResultsContentViewer_displayAttributes_type=Type", - "AnalysisResultsContentViewer_displayAttributes_configuration=Configuration", - "AnalysisResultsContentViewer_displayAttributes_conclusion=Conclusion" - }) - private static ResultDisplayAttributes getDisplayAttributes(AnalysisResult analysisResult) { - - String type = ""; - try { - type = normalizeAttr(analysisResult.getType().getDisplayName()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get type for analysis result with id: " + analysisResult.getArtifactID(), ex); - } - - Stream> baseAnalysisAttrs = Stream.of( - Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_score(), - normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), - Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_type(), - normalizeAttr(type)), - Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_configuration(), - normalizeAttr(analysisResult.getConfiguration())), - Pair.of(Bundle.AnalysisResultsContentViewer_displayAttributes_conclusion(), - normalizeAttr(analysisResult.getConclusion())) - ); - - Stream> blackboardAttributes = Stream.empty(); - try { - - blackboardAttributes = analysisResult.getAttributes().stream() - .filter(attr -> attr != null && attr.getAttributeType() != null && attr.getAttributeType().getDisplayName() != null) - .map(attr -> Pair.of(attr.getAttributeType().getDisplayName(), normalizeAttr(attr.getDisplayString()))) - .sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey())); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get attributes for analysis result with id: " + analysisResult.getArtifactID(), ex); - } - - List> allDisplayAttributes = Stream.concat(baseAnalysisAttrs, blackboardAttributes) - .collect(Collectors.toList()); - - return new ResultDisplayAttributes(analysisResult, allDisplayAttributes); - } - - private static List getScoreOrderedResults(Collection analysisResults) { - return analysisResults.stream() - .filter(ar -> ar != null && ar.getScore() != null) - // reverse order to push more important scores to the top - .sorted((a, b) -> -a.getScore().compareTo(b.getScore())) - .collect(Collectors.toList()); - } - - private static List getDisplayAttributes(Collection analysisResults) { - return analysisResults.stream() - .map(AnalysisResultsContentViewer::getDisplayAttributes) - .collect(Collectors.toList()); - } - - private static class NodeAnalysisResults { - - private final Collection analysisResults; - private final Optional selectedResult; - private final Optional aggregateScore; - - public NodeAnalysisResults(Collection analysisResults, Optional selectedResult, Optional aggregateScore) { - this.analysisResults = analysisResults; - this.selectedResult = selectedResult; - this.aggregateScore = aggregateScore; - } - - public Collection getAnalysisResults() { - return analysisResults; - } - - public Optional getSelectedResult() { - return selectedResult; - } - - public Optional getAggregateScore() { - return aggregateScore; - } - } - - private static NodeAnalysisResults getAnalysisResults(Node node) { - if (node == null) { - return new NodeAnalysisResults(Collections.emptyList(), Optional.empty(), Optional.empty()); - } - - Optional aggregateScore = Optional.empty(); - Map allAnalysisResults = new HashMap<>(); - Optional selectedResult = Optional.empty(); - - for (Content content : node.getLookup().lookupAll(Content.class)) { - if (content == null || content instanceof BlackboardArtifact) { - continue; - } - - try { - aggregateScore = Optional.ofNullable(content.getAggregateScore()); - - content.getAllAnalysisResults().stream() - .filter(ar -> ar != null) - .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); - - break; - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex); - } - } - - Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); - if (analysisResults.size() > 0) { - - List filteredResults = analysisResults.stream() - .filter(ar -> ar != null && ar.getScore() != null) - .collect(Collectors.toList()); - - filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); - - selectedResult = filteredResults.stream() - .max((a, b) -> a.getScore().compareTo(b.getScore())); - - if (!aggregateScore.isPresent()) { - aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore())); - } - } - - return new NodeAnalysisResults(getScoreOrderedResults(allAnalysisResults.values()), selectedResult, aggregateScore); - } - - private static Document render(List displayAttributes, Optional aggregateScore) { - Document html = Jsoup.parse(EMPTY_HTML); - Element body = html.getElementsByTag("body").first(); - - if (aggregateScore.isPresent()) { - appendAggregateScore(body, aggregateScore.get()); - } - - for (int idx = 0; idx < displayAttributes.size(); idx++) { - ResultDisplayAttributes resultAttrs = displayAttributes.get(idx); - appendResult(body, idx, resultAttrs); - } - - return html; - } - - @Messages("AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score") - private static void appendAggregateScore(Element body, Score score) { - appendSection(body, - MessageFormat.format("{0}: {1}", - Bundle.AnalysisResultsContentViewer_appendAggregateScore_displayKey(), - score.getSignificance().getDisplayName()), - Optional.empty()); - } - - private static String getAnchor(AnalysisResult analysisResult) { - return RESULT_ANCHOR_PREFIX + analysisResult.getId(); - } - - @Messages({"# {0} - analysisResultsNumber", - "AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0}" - }) - private static void appendResult(Element parent, int index, ResultDisplayAttributes attrs) { - Element sectionDiv = appendSection(parent, - Bundle.AnalysisResultsContentViewer_appendResult_headerKey(index + 1), - Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); - Element table = sectionDiv.appendElement("table"); - table.attr("class", SUBSECTION_CLASSNAME); - - Element tableBody = table.appendElement("tbody"); - - for (Pair keyVal : attrs.getAttributesToDisplay()) { - Element row = tableBody.appendElement("tr"); - String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":"; - row.appendElement("td").text(keyString); - String valueString = keyVal.getValue() == null ? "" : keyVal.getValue(); - row.appendElement("td").text(valueString); - } - } - - /** - * Appends a new section with a section header to the parent element. - * - * @param parent The element to append this section to. - * @param headerText The text for the section. - * @param anchorId The anchor id for this section. - * - * @return The div for the new section. - */ - private static Element appendSection(Element parent, String headerText, Optional anchorId) { - Element sectionDiv = parent.appendElement("div"); - if (anchorId.isPresent()) { - Element anchorEl = sectionDiv.appendElement("a"); - anchorEl.attr("name", anchorId.get()); - } - - sectionDiv.attr("class", SPACED_SECTION_CLASSNAME); - Element header = sectionDiv.appendElement("h1"); - header.text(headerText); - header.attr("class", HEADER_CLASSNAME); - return sectionDiv; - } - private SwingWorker worker = null; - /** - * Creates new form AnalysisResultsContentViewer - */ - public AnalysisResultsContentViewer() { - initComponents(); - textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS - HTMLEditorKit kit = new HTMLEditorKit(); - textPanel.setEditorKit(kit); - kit.getStyleSheet().addRule(STYLE_SHEET_RULE); - } @NbBundle.Messages({ "AnalysisResultsContentViewer_title=Analysis Results" @@ -363,123 +75,85 @@ public class AnalysisResultsContentViewer extends javax.swing.JPanel implements @Override public Component getComponent() { - return this; + return panel; } @Override public void resetComponent() { - textPanel.setText(""); + panel.reset(); } @Override - @Messages({ + @NbBundle.Messages({ "AnalysisResultsContentViewer_setNode_loadingMessage=Loading...", "AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results.",}) public synchronized void setNode(Node node) { - resetComponent(); + // reset the panel + panel.reset(); + // if there is a worker running, cancel it if (worker != null) { worker.cancel(true); worker = null; } + // if no node, nothing to do if (node == null) { return; } - showMessage(Bundle.AnalysisResultsContentViewer_setNode_loadingMessage()); + // show a loading message + panel.showMessage(Bundle.AnalysisResultsContentViewer_setNode_loadingMessage()); - worker = new DataFetchWorker( - (selectedNode) -> getAnalysisResults(selectedNode), + // create the worker + worker = new DataFetchWorker<>( + // load a view model from the node + (selectedNode) -> viewModel.getAnalysisResults(selectedNode), (nodeAnalysisResults) -> { - if (nodeAnalysisResults.getResultType() == ResultType.SUCCESS) { - displayResults(nodeAnalysisResults.getData()); + if (nodeAnalysisResults.getResultType() == DataFetchResult.ResultType.SUCCESS) { + // if successful, display the results + panel.displayResults(nodeAnalysisResults.getData()); } else { - showMessage(Bundle.AnalysisResultsContentViewer_setNode_errorMessage()); + // if there was an error, display an error message + panel.showMessage(Bundle.AnalysisResultsContentViewer_setNode_errorMessage()); } }, node); + // kick off the swing worker worker.execute(); } - private void showMessage(String message) { - textPanel.setText("" - + MessageFormat.format("

{1}

", MESSAGE_CLASSNAME, message) - + ""); - } - - private void displayResults(NodeAnalysisResults nodeResults) { - List displayAttributes = getDisplayAttributes(nodeResults.getAnalysisResults()); - Document document = render(displayAttributes, nodeResults.getAggregateScore()); - Optional selectedResult = nodeResults.getSelectedResult(); - textPanel.setText(document.html()); - - if (selectedResult.isPresent()) { - textPanel.scrollToReference(getAnchor(selectedResult.get())); - } - } - @Override public boolean isSupported(Node node) { if (node == null) { return false; } - AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); - if (abstractFile != null) { + // There needs to either be a file with an AnalysisResult or an AnalysisResult in the lookup. + for (Content content : node.getLookup().lookupAll(Content.class)) { + if (content instanceof AnalysisResult) { + return true; + } + + if (content == null || content instanceof BlackboardArtifact) { + continue; + } + try { - if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(abstractFile.getId())) { + if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) { return true; } } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + abstractFile.getId(), ex); + logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex); } } - - Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); - return (!analysisResults.isEmpty()); + + return false; } @Override public int isPreferred(Node node) { return PREFERRED_VALUE; } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); - textPanel = new javax.swing.JTextPane(); - - setPreferredSize(new java.awt.Dimension(100, 58)); - - textPanel.setEditable(false); - textPanel.setBackground(DEFAULT_BACKGROUND); - textPanel.setName(""); // NOI18N - textPanel.setPreferredSize(new java.awt.Dimension(600, 52)); - scrollPane.setViewportView(textPanel); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE) - ); - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JTextPane textPanel; - // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED index c3ca847cc4..f4558602d8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED @@ -1,11 +1,11 @@ -AnalysisResultsContentViewer_appendAggregateScore_displayKey=Aggregate Score +AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score # {0} - analysisResultsNumber -AnalysisResultsContentViewer_appendResult_headerKey=Analysis Result {0} -AnalysisResultsContentViewer_displayAttributes_conclusion=Conclusion -AnalysisResultsContentViewer_displayAttributes_configuration=Configuration -AnalysisResultsContentViewer_displayAttributes_score=Score -AnalysisResultsContentViewer_displayAttributes_type=Type +AnalysisResultsContentPanel_result_headerKey=Analysis Result {0} AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results. AnalysisResultsContentViewer_setNode_loadingMessage=Loading... AnalysisResultsContentViewer_title=Analysis Results AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node. +AnalysisResultsViewModel_displayAttributes_conclusion=Conclusion +AnalysisResultsViewModel_displayAttributes_configuration=Configuration +AnalysisResultsViewModel_displayAttributes_score=Score +AnalysisResultsViewModel_displayAttributes_type=Type From bdaa661b521dfb13a9253485917534c44894e8ee Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 15:13:47 -0400 Subject: [PATCH 69/78] refactor --- .../AnalysisResultsViewModel.java | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java new file mode 100644 index 0000000000..2ed547489f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java @@ -0,0 +1,278 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.contentviewers.analysisresults; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * @author gregd + */ +public class AnalysisResultsViewModel { + + private static final Logger logger = Logger.getLogger(AnalysisResultsViewModel.class.getName()); + + /** + * The attributes to display for a particular Analysis Result. + */ + static class ResultDisplayAttributes { + + private final AnalysisResult analysisResult; + private final List> attributesToDisplay; + + /** + * Constructor. + * + * @param analysisResult The analysis result which these attributes + * describe. + * @param attributesToDisplay The attributes to display in the order + * they should be displayed. + */ + ResultDisplayAttributes(AnalysisResult analysisResult, List> attributesToDisplay) { + this.analysisResult = analysisResult; + this.attributesToDisplay = attributesToDisplay; + } + + /** + * Returns the attributes to display. + * + * @return The attributes to display. + */ + List> getAttributesToDisplay() { + return attributesToDisplay; + } + + /** + * Returns the analysis result which these attributes describe. + * + * @return The analysis result. + */ + AnalysisResult getAnalysisResult() { + return analysisResult; + } + } + + /** + * The analysis results relating to a node (i.e. belonging to source content + * or directly in the lookup) to be displayed. + */ + static class NodeResults { + + private final List analysisResults; + private final Optional selectedResult; + private final Optional aggregateScore; + + /** + * Constructor. + * + * @param analysisResults The analysis results to be displayed. + * @param selectedResult The selected analysis result or empty if none + * selected. + * @param aggregateScore The aggregate score or empty if no score. + */ + NodeResults(List analysisResults, Optional selectedResult, Optional aggregateScore) { + this.analysisResults = analysisResults; + this.selectedResult = selectedResult; + this.aggregateScore = aggregateScore; + } + + /** + * Returns the analysis results to be displayed. + * + * @return The analysis results to be displayed. + */ + List getAnalysisResults() { + return analysisResults; + } + + /** + * Returns the selected analysis result or empty if none selected. + * + * @return The selected analysis result or empty if none selected. + */ + Optional getSelectedResult() { + return selectedResult; + } + + /** + * Returns the aggregate score or empty if no score. + * + * @return The aggregate score or empty if no score. + */ + Optional getAggregateScore() { + return aggregateScore; + } + } + + /** + * Normalizes the value of an attribute of an analysis result for display + * purposes. + * + * @param originalAttrStr The original attribute value. + * + * @return The normalized value for display. + */ + private String normalizeAttr(String originalAttrStr) { + return (originalAttrStr == null) ? "" : originalAttrStr.trim(); + } + + /** + * Returns the attributes to be displayed for an analysis result. + * + * @param analysisResult The analysis result. + * + * @return The attributes to be displayed. + */ + @NbBundle.Messages({ + "AnalysisResultsViewModel_displayAttributes_score=Score", + "AnalysisResultsViewModel_displayAttributes_type=Type", + "AnalysisResultsViewModel_displayAttributes_configuration=Configuration", + "AnalysisResultsViewModel_displayAttributes_conclusion=Conclusion" + }) + private ResultDisplayAttributes getDisplayAttributes(AnalysisResult analysisResult) { + // The type of BlackboardArtifact.Type of the analysis result. + String type = ""; + try { + type = normalizeAttr(analysisResult.getType().getDisplayName()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get type for analysis result with id: " + analysisResult.getArtifactID(), ex); + } + + // The standard attributes to display (score, type, configuration, conclusion) + Stream> baseAnalysisAttrs = Stream.of( + Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_score(), + normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())), + Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_type(), + normalizeAttr(type)), + Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_configuration(), + normalizeAttr(analysisResult.getConfiguration())), + Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_conclusion(), + normalizeAttr(analysisResult.getConclusion())) + ); + + // The BlackboardAttributes sorted by type display name. + Stream> blackboardAttributes = Stream.empty(); + try { + + blackboardAttributes = analysisResult.getAttributes().stream() + .filter(attr -> attr != null && attr.getAttributeType() != null && attr.getAttributeType().getDisplayName() != null) + .map(attr -> Pair.of(attr.getAttributeType().getDisplayName(), normalizeAttr(attr.getDisplayString()))) + .sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey())); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get attributes for analysis result with id: " + analysisResult.getArtifactID(), ex); + } + + // return the standard attributes along with the key value pairs of the BlackboardAttribute values. + List> allDisplayAttributes = Stream.concat(baseAnalysisAttrs, blackboardAttributes) + .collect(Collectors.toList()); + + return new ResultDisplayAttributes(analysisResult, allDisplayAttributes); + } + + private List getOrderedDisplayAttributes(Collection analysisResults) { + return analysisResults.stream() + .filter(ar -> ar != null && ar.getScore() != null) + // reverse order to push more important scores to the top + .sorted((a, b) -> -a.getScore().compareTo(b.getScore())) + .map((ar) -> getDisplayAttributes(ar)) + .collect(Collectors.toList()); + } + + /** + * Returns the view model data representing the analysis results to be + * displayed for the node. + * + * @param node The node. + * + * @return The analysis results view model data to display. + */ + NodeResults getAnalysisResults(Node node) { + if (node == null) { + return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty()); + } + + Optional aggregateScore = Optional.empty(); + // maps id of analysis result to analysis result to prevent duplication + Map allAnalysisResults = new HashMap<>(); + Optional selectedResult = Optional.empty(); + + // Find first content that is not an artifact within node + for (Content content : node.getLookup().lookupAll(Content.class)) { + if (content == null || content instanceof BlackboardArtifact) { + continue; + } + + try { + // get the aggregate score of that content + aggregateScore = Optional.ofNullable(content.getAggregateScore()); + + // and add all analysis results to mapping + content.getAllAnalysisResults().stream() + .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + + break; + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex); + } + } + + // Find any analysis results in the node + Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); + if (analysisResults.size() > 0) { + + // get any items with a score + List filteredResults = analysisResults.stream() + .collect(Collectors.toList()); + + // add them to the map to display + filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); + + // the selected result will be the highest scored analysis result in the node. + selectedResult = filteredResults.stream() + .max((a, b) -> a.getScore().compareTo(b.getScore())); + + // if no aggregate score determined at this point, use the selected result score. + if (!aggregateScore.isPresent()) { + aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore())); + } + } + + // get view model representation + List displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values()); + + return new NodeResults(displayAttributes, selectedResult, aggregateScore); + } +} From 0ad67e703f462f8ca55b57d09323e3b8cfe8bea2 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 27 May 2021 15:15:45 -0400 Subject: [PATCH 70/78] comment update --- .../analysisresults/AnalysisResultsViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java index 2ed547489f..8ba7308eb4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java @@ -39,7 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * - * @author gregd + * Creates a representation of a list of analysis results gathered from a node. */ public class AnalysisResultsViewModel { From 40472bf8b2080dd25b8ccef2426fb7b360521426 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 27 May 2021 17:14:54 -0400 Subject: [PATCH 71/78] deprecated the time format methods in ContentUtils --- .../autopsy/contentviewers/Metadata.java | 13 ++-- .../DefaultTableArtifactContentViewer.java | 25 ++----- .../GeneralPurposeArtifactViewer.java | 11 ++- .../autopsy/coreutils/TimeZoneUtils.java | 68 +++++++++++++++---- .../datamodel/AbstractAbstractFileNode.java | 19 +++--- .../datamodel/ArtifactStringContent.java | 13 ++-- .../datamodel/BlackboardArtifactNode.java | 15 ++-- .../autopsy/datamodel/ContentTagNode.java | 11 +-- .../autopsy/datamodel/ContentUtils.java | 58 +++++++--------- .../autopsy/datamodel/KeywordHits.java | 9 +-- .../discovery/search/DiscoveryKeyUtils.java | 6 +- .../discovery/search/DomainSearch.java | 9 +-- .../discovery/ui/ArtifactsListPanel.java | 9 ++- .../discovery/ui/DomainSummaryPanel.java | 4 +- .../infrastructure/TableReportGenerator.java | 5 +- .../textextractors/ArtifactTextExtractor.java | 7 +- .../autopsy/keywordsearch/Ingester.java | 9 +-- 17 files changed, 150 insertions(+), 141 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 2c695dfcb4..eacf66b296 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2013-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,13 +26,12 @@ import java.util.logging.Level; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; @@ -299,10 +298,10 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize())); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString()); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), ContentUtils.getStringTime(file.getMtime(), file)); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file)); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file)); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file)); + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), TimeZoneUtils.getFormattedTime(file.getMtime())); + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), TimeZoneUtils.getFormattedTime(file.getAtime())); + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), TimeZoneUtils.getFormattedTime(file.getCrtime())); + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), TimeZoneUtils.getFormattedTime(file.getCtime())); String md5 = file.getMd5Hash(); if (md5 == null) { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/DefaultTableArtifactContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/DefaultTableArtifactContentViewer.java index 0c90993a61..75882a39b0 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/DefaultTableArtifactContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/DefaultTableArtifactContentViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2020 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,6 @@ import javax.swing.text.View; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; @@ -54,6 +53,7 @@ import com.google.gson.JsonArray; import java.util.Locale; import java.util.Map; import javax.swing.SwingUtilities; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel; //import org.sleuthkit.autopsy.contentviewers.Bundle; @@ -343,7 +343,7 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa // Use Autopsy date formatting settings, not TSK defaults case DATETIME: - value = epochTimeToString(attr.getValueLong()); + value = TimeZoneUtils.getFormattedTime(attr.getValueLong()); break; case JSON: // Get the attribute's JSON value and convert to indented multiline display string @@ -454,7 +454,7 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa String attributeName = jsonKey; String attributeValue; if (attributeName.toUpperCase().contains("DATETIME")) { - attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString())); + attributeValue = TimeZoneUtils.getFormattedTime(Long.parseLong(jsonElement.getAsString())); } else { attributeValue = jsonElement.getAsString(); } @@ -463,23 +463,6 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey)); } } - - /** - * Converts epoch time to readable string. - * - * @param epochTime epoch time value to be converted to string. - * - * @return String with human readable time. - */ - private String epochTimeToString(long epochTime) { - String dateTimeString = "0000-00-00 00:00:00"; - if (null != content && 0 != epochTime) { - dateFormatter.setTimeZone(ContentUtils.getTimeZone(content)); - dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000)); - } - return dateTimeString; - } - } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java index 1b07ecd754..d8150a91a2 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java @@ -1,7 +1,7 @@ /* * Autopsy * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,11 +44,10 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; /** @@ -263,9 +262,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i for (BlackboardAttribute bba : attrList) { if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) { - addNameValueRow(Bundle.GeneralPurposeArtifactViewer_dates_time(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); + addNameValueRow(Bundle.GeneralPurposeArtifactViewer_dates_time(), TimeZoneUtils.getFormattedTime(bba.getValueLong())); } else { - addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); + addNameValueRow(bba.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(bba.getValueLong())); } } else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) { addNameValueRow(Bundle.GeneralPurposeArtifactViewer_term_label(), bba.getDisplayString()); @@ -293,7 +292,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i for (int key : attributeMap.keySet()) { for (BlackboardAttribute bba : attributeMap.get(key)) { if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { - addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); + addNameValueRow(bba.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(bba.getValueLong())); } else { addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString()); } diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java index c844d9ce97..5f2ac6d4cc 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,8 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.SimpleTimeZone; import java.util.TimeZone; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.datamodel.TimeUtilities; /** * Utility methods for workig with time zones. @@ -52,7 +54,7 @@ public class TimeZoneUtils { DateFormat dfm = new SimpleDateFormat("z"); dfm.setTimeZone(zone); boolean hasDaylight = zone.useDaylightTime(); - String first = dfm.format(new GregorianCalendar(2010, 1, 1).getTime()).substring(0, 3); + String first = dfm.format(new GregorianCalendar(2010, 1, 1).getTime()).substring(0, 3); String second = dfm.format(new GregorianCalendar(2011, 6, 6).getTime()).substring(0, 3); int mid = hour * -1; String result = first + Integer.toString(mid); @@ -65,19 +67,19 @@ public class TimeZoneUtils { return result; } - + /** * Generate a time zone string containing the GMT offset and ID. - * + * * @param timeZone The time zone. - * + * * @return The time zone string. */ public static String createTimeZoneString(TimeZone timeZone) { int offset = timeZone.getRawOffset() / 1000; int hour = offset / 3600; int minutes = Math.abs((offset % 3600) / 60); - + return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS } @@ -89,7 +91,7 @@ public class TimeZoneUtils { * Create a list of time zones. */ List timeZoneList = new ArrayList<>(); - + String[] ids = SimpleTimeZone.getAvailableIDs(); for (String id : ids) { /* @@ -103,36 +105,72 @@ public class TimeZoneUtils { */ timeZoneList.add(TimeZone.getTimeZone(id)); } - + /* * Sort the list of time zones first by offset, then by ID. */ - Collections.sort(timeZoneList, new Comparator(){ + Collections.sort(timeZoneList, new Comparator() { @Override - public int compare(TimeZone o1, TimeZone o2){ + public int compare(TimeZone o1, TimeZone o2) { int offsetDelta = Integer.compare(o1.getRawOffset(), o2.getRawOffset()); - + if (offsetDelta == 0) { return o1.getID().compareToIgnoreCase(o2.getID()); } - + return offsetDelta; } }); - + /* * Create a list of Strings encompassing both the GMT offset and the * time zone ID. */ List outputList = new ArrayList<>(); - + for (TimeZone timeZone : timeZoneList) { outputList.add(createTimeZoneString(timeZone)); } - + return outputList; } + /** + * Returns the time formatted in the user selected time zone. + * + * @param epochTime + * + * @return + */ + public static String getFormattedTime(long epochTime) { + return TimeUtilities.epochToTime(epochTime, getTimeZone()); + } + + /** + * Returns the formatted time in the user selected time zone in ISO8601 + * format. + * + * @param epochTime Seconds from java epoch + * + * @return Formatted date time string in ISO8601 + */ + public static String getFormattedTimeISO8601(long epochTime) { + return TimeUtilities.epochToTimeISO8601(epochTime, getTimeZone()); + } + + /** + * Returns the user preferred timezone. + * + * @return TimeZone to use when formatting time values. + */ + public static TimeZone getTimeZone() { + if (UserPreferences.displayTimesInLocalTime()) { + return TimeZone.getDefault(); + } + + return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()); + } + /** * Prevents instantiation. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index ee862eee96..a56beed018 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2020 Basis Technology Corp. + * Copyright 2012-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,6 +66,7 @@ import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil; /** @@ -350,10 +351,10 @@ public abstract class AbstractAbstractFileNode extends A new WeakReference<>(this), weakPcl)); } - properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content))); - properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content))); - properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getAtime(), content))); - properties.add(new NodeProperty<>(CREATED_TIME.toString(), CREATED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCrtime(), content))); + properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getMtime()))); + properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getCtime()))); + properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getAtime()))); + properties.add(new NodeProperty<>(CREATED_TIME.toString(), CREATED_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getCrtime()))); properties.add(new NodeProperty<>(SIZE.toString(), SIZE.toString(), NO_DESCR, content.getSize())); properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString())); properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString())); @@ -571,10 +572,10 @@ public abstract class AbstractAbstractFileNode extends A static public void fillPropertyMap(Map map, AbstractFile content) { map.put(NAME.toString(), getContentDisplayName(content)); map.put(LOCATION.toString(), getContentPath(content)); - map.put(MOD_TIME.toString(), ContentUtils.getStringTime(content.getMtime(), content)); - map.put(CHANGED_TIME.toString(), ContentUtils.getStringTime(content.getCtime(), content)); - map.put(ACCESS_TIME.toString(), ContentUtils.getStringTime(content.getAtime(), content)); - map.put(CREATED_TIME.toString(), ContentUtils.getStringTime(content.getCrtime(), content)); + map.put(MOD_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getMtime())); + map.put(CHANGED_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getCtime())); + map.put(ACCESS_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getAtime())); + map.put(CREATED_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getCrtime())); map.put(SIZE.toString(), content.getSize()); map.put(FLAGS_DIR.toString(), content.getDirFlagAsString()); map.put(FLAGS_META.toString(), content.getMetaFlagsAsString()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index e2950e5b12..ea3fa67060 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,12 @@ */ package org.sleuthkit.autopsy.datamodel; -import java.text.SimpleDateFormat; import java.util.logging.Level; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; @@ -39,8 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; */ @Deprecated public class ArtifactStringContent implements StringContent { - - private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private final static Logger logger = Logger.getLogger(ArtifactStringContent.class.getName()); private final BlackboardArtifact artifact; private String stringContent = ""; @@ -130,11 +129,7 @@ public class ArtifactStringContent implements StringContent { // Use Autopsy date formatting settings, not TSK defaults case DATETIME: long epoch = attr.getValueLong(); - value = "0000-00-00 00:00:00"; - if (null != content && 0 != epoch) { - dateFormatter.setTimeZone(ContentUtils.getTimeZone(content)); - value = dateFormatter.format(new java.util.Date(epoch * 1000)); - } + value = TimeZoneUtils.getFormattedTime(epoch * 1000); break; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 5bfca75425..437e354cdf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2020 Basis Technology Corp. + * Copyright 2012-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -78,6 +78,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.datamodel.utils.IconsUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; @@ -680,22 +681,22 @@ public class BlackboardArtifactNode extends AbstractContentNode( NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"), "", - file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file))); + file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime()))); sheetSet.put(new NodeProperty<>( NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"), "", - file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file))); + file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime()))); sheetSet.put(new NodeProperty<>( NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"), "", - file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file))); + file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime()))); sheetSet.put(new NodeProperty<>( NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"), @@ -1033,7 +1034,7 @@ public class BlackboardArtifactNode extends AbstractContentNode sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; @@ -96,22 +97,22 @@ class ContentTagNode extends TagNode { properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"), "", - file != null ? ContentUtils.getStringTime(file.getMtime(), file) : "")); + file != null ? TimeZoneUtils.getFormattedTime(file.getMtime()) : "")); properties.put(new NodeProperty<>( NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"), "", - file != null ? ContentUtils.getStringTime(file.getCtime(), file) : "")); + file != null ? TimeZoneUtils.getFormattedTime(file.getCtime()) : "")); properties.put(new NodeProperty<>( NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"), "", - file != null ? ContentUtils.getStringTime(file.getAtime(), file) : "")); + file != null ? TimeZoneUtils.getFormattedTime(file.getAtime()) : "")); properties.put(new NodeProperty<>( NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"), "", - file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : "")); + file != null ? TimeZoneUtils.getFormattedTime(file.getCrtime()) : "")); properties.put(new NodeProperty<>( NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java index 34ebfac8a6..6def78b83d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java @@ -26,8 +26,6 @@ import java.util.TimeZone; import java.util.concurrent.Future; import java.util.function.Supplier; import java.util.logging.Level; -import java.util.prefs.PreferenceChangeEvent; -import java.util.prefs.PreferenceChangeListener; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; @@ -39,7 +37,6 @@ import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.LocalDirectory; @@ -55,21 +52,9 @@ import org.sleuthkit.datamodel.VirtualDirectory; public final class ContentUtils { private final static Logger logger = Logger.getLogger(ContentUtils.class.getName()); - private static boolean displayTimesInLocalTime = UserPreferences.displayTimesInLocalTime(); private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); private static final SimpleDateFormat dateFormatterISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - static { - UserPreferences.addChangeListener(new PreferenceChangeListener() { - @Override - public void preferenceChange(PreferenceChangeEvent evt) { - if (evt.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) { - displayTimesInLocalTime = UserPreferences.displayTimesInLocalTime(); - } - } - }); - } - /** * Don't instantiate */ @@ -85,6 +70,7 @@ public final class ContentUtils { * * @return The time */ + @Deprecated public static String getStringTime(long epochSeconds, TimeZone tzone) { String time = "0000-00-00 00:00:00"; if (epochSeconds != 0) { @@ -104,6 +90,7 @@ public final class ContentUtils { * * @return The time */ + @Deprecated public static String getStringTimeISO8601(long epochSeconds, TimeZone tzone) { String time = "0000-00-00T00:00:00Z"; //NON-NLS if (epochSeconds != 0) { @@ -123,7 +110,10 @@ public final class ContentUtils { * @param content * * @return + * + * @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getFormattedTime instead */ + @Deprecated public static String getStringTime(long epochSeconds, Content content) { return getStringTime(epochSeconds, getTimeZone(content)); } @@ -136,29 +126,30 @@ public final class ContentUtils { * @param c * * @return + * + * @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getFormattedTimeISO8601 instead */ + @Deprecated public static String getStringTimeISO8601(long epochSeconds, Content c) { return getStringTimeISO8601(epochSeconds, getTimeZone(c)); } + /** + * Returns either the user selected time zone or the system time zone. + * + * @param content + * + * @return + * + * @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getTimeZone instead + */ + @Deprecated public static TimeZone getTimeZone(Content content) { - - try { - if (!shouldDisplayTimesInLocalTime()) { - return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()); - } else { - final Content dataSource = content.getDataSource(); - if ((dataSource != null) && (dataSource instanceof Image)) { - Image image = (Image) dataSource; - return TimeZone.getTimeZone(image.getTimeZone()); - } else { - //case such as top level VirtualDirectory - return TimeZone.getDefault(); - } - } - } catch (TskCoreException ex) { + if (!shouldDisplayTimesInLocalTime()) { + return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()); + } else { return TimeZone.getDefault(); - } + } } private static final SystemNameVisitor systemName = new SystemNameVisitor(); @@ -553,9 +544,12 @@ public final class ContentUtils { * Indicates whether or not times should be displayed using local time. * * @return True or false. + * + * @deprecated Call UserPreferences.displayTimesInLocalTime instead. */ + @Deprecated public static boolean shouldDisplayTimesInLocalTime() { - return displayTimesInLocalTime; + return UserPreferences.displayTimesInLocalTime(); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index eab8a07d3a..9201ea5f70 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2020 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -46,6 +46,7 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -888,17 +889,17 @@ public class KeywordHits implements AutopsyVisitableItem { KeywordHits_createNodeForKey_modTime_name(), KeywordHits_createNodeForKey_modTime_displayName(), KeywordHits_createNodeForKey_modTime_desc(), - ContentUtils.getStringTime(file.getMtime(), file))); + TimeZoneUtils.getFormattedTime(file.getMtime()))); n.addNodeProperty(new NodeProperty<>( KeywordHits_createNodeForKey_accessTime_name(), KeywordHits_createNodeForKey_accessTime_displayName(), KeywordHits_createNodeForKey_accessTime_desc(), - ContentUtils.getStringTime(file.getAtime(), file))); + TimeZoneUtils.getFormattedTime(file.getAtime()))); n.addNodeProperty(new NodeProperty<>( KeywordHits_createNodeForKey_chgTime_name(), KeywordHits_createNodeForKey_chgTime_displayName(), KeywordHits_createNodeForKey_chgTime_desc(), - ContentUtils.getStringTime(file.getCtime(), file))); + TimeZoneUtils.getFormattedTime(file.getCtime()))); return n; } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java index 39b3c1b80c..c201414199 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java @@ -1,7 +1,7 @@ /* * Autopsy * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,7 +32,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.discovery.search.SearchData.PageViews; import org.sleuthkit.autopsy.discovery.ui.MonthAbbreviation; import org.sleuthkit.datamodel.AbstractFile; @@ -1217,7 +1217,7 @@ public class DiscoveryKeyUtils { Instant startActivityAsInsant = Instant.ofEpochSecond(epochSeconds); // Determines the timezone using the settings panel or value parsed from the // parent data source - TimeZone currentTimeZone = ContentUtils.getTimeZone(domainResult.getDataSource()); + TimeZone currentTimeZone = TimeZoneUtils.getTimeZone(); // Convert to a datetime using epoch and timezone. ZonedDateTime startActivityAsDateTime = ZonedDateTime.ofInstant(startActivityAsInsant, currentTimeZone.toZoneId()); // Get the closest Sunday, which is the cut off for the current week. diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java index 497d622b1c..eb52327d45 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java @@ -1,7 +1,7 @@ /* * Autopsy * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,15 +24,13 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TimeZone; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; /** @@ -246,8 +244,7 @@ public class DomainSearch { private String getDate(BlackboardArtifact artifact) throws TskCoreException { for (BlackboardAttribute attribute : artifact.getAttributes()) { if (attribute.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { - TimeZone timeZone = ContentUtils.getTimeZone(artifact); - String dateString = TimeUtilities.epochToTime(attribute.getValueLong(), timeZone); + String dateString = TimeZoneUtils.getFormattedTime(attribute.getValueLong()); if (dateString.length() >= 10) { return dateString.substring(0, 10); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java index cbf38d7453..dac2668c50 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java @@ -1,7 +1,7 @@ /* * Autopsy * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,11 +33,10 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; /** @@ -303,7 +302,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private String getStringForColumn(BlackboardArtifact artifact, BlackboardAttribute bba, int columnIndex) throws TskCoreException { if (columnIndex == 0 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) { - return TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact)); + return TimeZoneUtils.getFormattedTime(bba.getValueLong()); } else if (columnIndex == 1) { if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) { if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) { @@ -339,7 +338,7 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { final BlackboardArtifact artifact = getArtifactByRow(rowIndex); for (BlackboardAttribute bba : artifact.getAttributes()) { if (columnIndex == 0 && bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME") && !StringUtils.isBlank(bba.getDisplayString())) { - return TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact)); + return TimeZoneUtils.getFormattedTime(bba.getValueLong()); } else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) { return bba.getDisplayString(); } else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) { diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java index d78cb5e2f3..661ba0edca 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java @@ -33,7 +33,7 @@ import javax.swing.JList; import javax.swing.ListCellRenderer; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.discovery.search.SearchData; /** @@ -180,7 +180,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer< @Override public Component getListCellRendererComponent(JList list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) { domainNameLabel.setText(value.getResultDomain().getDomain()); - TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource()); + TimeZone timeZone = TimeZoneUtils.getTimeZone(); String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone); String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone); String notability = Bundle.DomainSummaryPanel_notability_text(); diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java index 34186c13b2..93f7f406d2 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2020 Basis Technology Corp. + * Copyright 2013-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,6 +48,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.report.ReportProgressPanel; import static org.sleuthkit.autopsy.casemodule.services.TagsManager.getNotableTagLabel; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -1919,7 +1920,7 @@ class TableReportGenerator { if (attribute.getAttributeType().getValueType() != BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { return attribute.getDisplayString(); } else { - return ContentUtils.getStringTime(attribute.getValueLong(), artData.getContent()); + return TimeZoneUtils.getFormattedTime(attribute.getValueLong()); } } } diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java index 43c0112f1a..184cd064b6 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/ArtifactTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,10 +22,9 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; -import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** @@ -57,7 +56,7 @@ class ArtifactTextExtractor implements TextExtractor { // in the Autopsy datamodel. switch (attribute.getValueType()) { case DATETIME: - artifactContents.append(ContentUtils.getStringTime(attribute.getValueLong(), artifact)); + artifactContents.append(TimeZoneUtils.getFormattedTime(attribute.getValueLong())); break; default: artifactContents.append(attribute.getDisplayString()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index 24a42041dd..0cc52149b7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -30,6 +30,7 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.common.SolrInputDocument; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.healthmonitor.HealthMonitor; import org.sleuthkit.autopsy.healthmonitor.TimingMetric; @@ -389,10 +390,10 @@ class Ingester { */ private Map getCommonAndMACTimeFields(AbstractFile file) { Map params = getCommonFields(file); - params.put(Server.Schema.CTIME.toString(), ContentUtils.getStringTimeISO8601(file.getCtime(), file)); - params.put(Server.Schema.ATIME.toString(), ContentUtils.getStringTimeISO8601(file.getAtime(), file)); - params.put(Server.Schema.MTIME.toString(), ContentUtils.getStringTimeISO8601(file.getMtime(), file)); - params.put(Server.Schema.CRTIME.toString(), ContentUtils.getStringTimeISO8601(file.getCrtime(), file)); + params.put(Server.Schema.CTIME.toString(), TimeZoneUtils.getFormattedTimeISO8601(file.getCtime())); + params.put(Server.Schema.ATIME.toString(), TimeZoneUtils.getFormattedTimeISO8601(file.getAtime())); + params.put(Server.Schema.MTIME.toString(), TimeZoneUtils.getFormattedTimeISO8601(file.getMtime())); + params.put(Server.Schema.CRTIME.toString(), TimeZoneUtils.getFormattedTimeISO8601(file.getCrtime())); return params; } From e0d637d2833e59703c9b6073a3b47a3c112ac626 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 27 May 2021 18:03:51 -0400 Subject: [PATCH 72/78] OsAccountNode needs to use the new time format methods --- Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 170251bd13..e3bcd6e52a 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.datamodel.Host; @@ -301,7 +302,7 @@ public final class OsAccounts implements AutopsyVisitableItem { Optional creationTimeValue = account.getCreationTime(); String timeDisplayStr - = creationTimeValue.isPresent() ? DATE_FORMATTER.format(new java.util.Date(creationTimeValue.get() * 1000)) : ""; + = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get() * 1000) : ""; propertiesSet.put(new NodeProperty<>( Bundle.OsAccounts_createdTimeProperty_name(), From 1d13edfc24f1b18b3fc8120189f2c0b654946a9d Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 28 May 2021 08:14:18 -0400 Subject: [PATCH 73/78] updates to tskdbdiff.py for EFE --- test/script/tskdbdiff.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/script/tskdbdiff.py b/test/script/tskdbdiff.py index ac0f4cb044..796d0f1f9f 100644 --- a/test/script/tskdbdiff.py +++ b/test/script/tskdbdiff.py @@ -924,13 +924,9 @@ def normalize_tsk_files_path(guid_util: TskGuidUtils, row: Dict[str, any]) -> Di if module_output_idx >= 0: # remove everything up to and including ModuleOutput if ModuleOutput present path_parts = path_parts[module_output_idx:] - if len(path_parts) > 1 and path_parts[1] == 'Embedded File Extractor': - # Takes a folder like ModuleOutput\Embedded File Extractor/f_000168_4435\f_000168 - # and fixes the folder after 'Embedded File Extractor', 'f_000168_4435' to remove the last number - # to become 'f_000168' - match = re.match(r'^(.+?)_\d*$', path_parts[2]) - if match: - path_parts[2] = match.group(1) + if len(path_parts) > 2 and path_parts[1] == 'EFE': + # for embedded file extractor, the next folder is the object id and should be omitted + del path_parts[2] row_copy['path'] = os.path.join(*path_parts) if len(path_parts) > 0 else '/' From 483b96b9640253807d4f2fb3ce3d013bdb5c42a3 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 28 May 2021 13:13:37 -0400 Subject: [PATCH 74/78] Fixed two places still called contentutils in IG --- .../autopsy/imagegallery/datamodel/DrawableAttribute.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java index 03ea2e3292..5bf810edcd 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableAttribute.java @@ -33,6 +33,7 @@ import javafx.scene.image.ImageView; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.TagName; @@ -118,13 +119,13 @@ public class DrawableAttribute> { = new DrawableAttribute<>(AttributeName.CREATED_TIME, Bundle.DrawableAttribute_createdTime(), true, "clock--plus.png", //NON-NLS - f -> Collections.singleton(ContentUtils.getStringTime(f.getCrtime(), f.getAbstractFile()))); + f -> Collections.singleton(TimeZoneUtils.getFormattedTime(f.getCrtime()))); public final static DrawableAttribute MODIFIED_TIME = new DrawableAttribute<>(AttributeName.MODIFIED_TIME, Bundle.DrawableAttribute_modifiedTime(), true, "clock--pencil.png", //NON-NLS - f -> Collections.singleton(ContentUtils.getStringTime(f.getMtime(), f.getAbstractFile()))); + f -> Collections.singleton(TimeZoneUtils.getFormattedTime(f.getMtime()))); public final static DrawableAttribute MAKE = new DrawableAttribute<>(AttributeName.MAKE, Bundle.DrawableAttribute_cameraMake(), From ef741d60d080fd238c9b1c65fce9c14d093ccd88 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 28 May 2021 17:08:18 -0400 Subject: [PATCH 75/78] 7660 make tables have uniform look and feel --- .../casemodule/IngestJobInfoPanel.form | 18 ++--- .../casemodule/IngestJobInfoPanel.java | 5 +- .../ui/DataSourceBrowser.java | 1 - .../uiutils/BaseMessageOverlay.java | 1 - .../uiutils/CellModelTableCellRenderer.java | 38 ++--------- .../uiutils/DefaultCellModel.java | 65 +++++++------------ .../uiutils/GuiCellModel.java | 6 -- .../uiutils/JTablePanel.java | 33 ++++++---- 8 files changed, 58 insertions(+), 109 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form index c183e0de36..4e74529073 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form @@ -1,11 +1,6 @@ - - - - - @@ -16,6 +11,7 @@ + @@ -35,9 +31,6 @@ - - - @@ -60,9 +53,6 @@ - - - @@ -85,6 +75,9 @@ + + + @@ -157,6 +150,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index 4dcdc5260f..c0afb624e7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -267,19 +267,17 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { javax.swing.JScrollPane ingestModulesScrollPane = new javax.swing.JScrollPane(); ingestModuleTable = new javax.swing.JTable(); - setMaximumSize(new java.awt.Dimension(32767, 32767)); setLayout(new java.awt.BorderLayout()); - contentPanel.setMaximumSize(new java.awt.Dimension(32767, 32767)); contentPanel.setMinimumSize(new java.awt.Dimension(625, 150)); contentPanel.setPreferredSize(new java.awt.Dimension(625, 150)); contentPanel.setLayout(new java.awt.GridBagLayout()); ingestJobsScrollPane.setBorder(null); ingestJobsScrollPane.setMinimumSize(new java.awt.Dimension(16, 16)); - ingestJobsScrollPane.setPreferredSize(null); ingestJobTable.setModel(ingestJobTableModel); + ingestJobTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor")); ingestJobTable.getTableHeader().setReorderingAllowed(false); ingestJobsScrollPane.setViewportView(ingestJobTable); ingestJobTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); @@ -315,6 +313,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { ingestModulesScrollPane.setPreferredSize(new java.awt.Dimension(254, 16)); ingestModuleTable.setModel(ingestModuleTableModel); + ingestModuleTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor")); ingestModulesScrollPane.setViewportView(ingestModuleTable); gridBagConstraints = new java.awt.GridBagConstraints(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java index 98522e5071..bb945833aa 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java @@ -80,7 +80,6 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_tags_header(), Bundle.DataSourceSummaryNode_column_tags_header()); outline = outlineView.getOutline(); - outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); dataSourceSummaryList = getDataSourceSummaryList(usageMap, fileCountsMap); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java index 8e90945eb7..634bb159cc 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java @@ -26,7 +26,6 @@ import javax.swing.JLabel; * painting a JLabel using a java.awt.Graphics object. */ public class BaseMessageOverlay { - private final JLabel label; private boolean visible = false; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 2638ce1f56..1f18e23e30 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.Component; -import java.awt.Insets; import java.awt.event.MouseEvent; import java.util.List; import javax.swing.BorderFactory; @@ -30,7 +29,6 @@ import javax.swing.JTable; import javax.swing.border.Border; import javax.swing.table.DefaultTableCellRenderer; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseEvent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseListener; @@ -43,7 +41,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; private static final int DEFAULT_ALIGNMENT = JLabel.LEFT; - private static final Border DEFAULT_BORDER = BorderFactory.createEmptyBorder(1, 5, 1, 5); + private static final Border DEFAULT_BORDER = BorderFactory.createEmptyBorder(2, 4, 2, 4); @Override public Component getTableCellRendererComponent(JTable table, Object value, @@ -62,43 +60,21 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * Customizes the jlabel to match the column model and cell model provided. * * @param defaultCell The cell to customize that will be displayed in the - * jtable. - * @param cellModel The cell model for this cell. + * jtable. + * @param cellModel The cell model for this cell. * * @return The provided defaultCell. */ protected Component getTableCellRendererComponent(JLabel defaultCell, GuiCellModel cellModel) { - // sets the text for the cell or null if not present. - String text = cellModel.getText(); - if (StringUtils.isNotBlank(text)) { - defaultCell.setText(text); - } else { - defaultCell.setText(null); - } - - // sets the tooltip for the cell if present. - String tooltip = cellModel.getTooltip(); - if (StringUtils.isNotBlank(tooltip)) { - defaultCell.setToolTipText(tooltip); - } else { - defaultCell.setToolTipText(null); - } - - // sets the padding for cell text within the cell. - Insets insets = cellModel.getInsets(); - if (insets != null) { - defaultCell.setBorder(BorderFactory.createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); - } else { - defaultCell.setBorder(DEFAULT_BORDER); - } - + defaultCell.setText(cellModel.getText()); + defaultCell.setToolTipText(cellModel.getTooltip()); // sets the JLabel alignment (left, center, right) or default alignment // if no alignment is specified int alignment = (cellModel.getHorizontalAlignment() == null) ? DEFAULT_ALIGNMENT : cellModel.getHorizontalAlignment().getJLabelAlignment(); defaultCell.setHorizontalAlignment(alignment); - + defaultCell.setBorder(DEFAULT_BORDER); return defaultCell; } @@ -134,7 +110,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { /** * @return The default cell mouse listener that triggers popups for - * non-primary button events. + * non-primary button events. */ public static CellMouseListener getMouseListener() { return DEFAULT_CELL_MOUSE_LISTENER; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java index 06165a3023..215f71469e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java @@ -18,27 +18,24 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; -import java.awt.Insets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel; /** * The default cell model. */ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { - final T data; - final Function stringConverter; - String tooltip; - CellModel.HorizontalAlign horizontalAlignment; - Insets insets; - List popupMenu; - Supplier> menuItemSupplier; - final String excelFormatString; + private final T data; + private final String text; + private String tooltip; + private CellModel.HorizontalAlign horizontalAlignment; + private List popupMenu; + private Supplier> menuItemSupplier; + private final String excelFormatString; /** * Main constructor. @@ -52,9 +49,9 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { /** * Constructor. * - * @param data The data to be displayed in the cell. + * @param data The data to be displayed in the cell. * @param stringConverter The means of converting that data to a string or - * null to use .toString method on object. + * null to use .toString method on object. */ public DefaultCellModel(T data, Function stringConverter) { this(data, stringConverter, null); @@ -63,20 +60,25 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { /** * Constructor. * - * @param data The data to be displayed in the cell. - * @param stringConverter The means of converting that data to a string or - * null to use .toString method on object. + * @param data The data to be displayed in the cell. + * @param stringConverter The means of converting that data to a string or + * null to use .toString method on object. * @param excelFormatString The apache poi excel format string to use with - * the data. + * the data. * * NOTE: Only certain data types can be exported. See * ExcelTableExport.createCell() for types. */ public DefaultCellModel(T data, Function stringConverter, String excelFormatString) { this.data = data; - this.stringConverter = stringConverter; this.excelFormatString = excelFormatString; - this.tooltip = getText(); + + if (stringConverter == null) { + text = this.data == null ? "" : this.data.toString(); + } else { + text = stringConverter.apply(this.data); + } + this.tooltip = text; } @Override @@ -91,11 +93,7 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { @Override public String getText() { - if (this.stringConverter == null) { - return this.data == null ? "" : this.data.toString(); - } else { - return this.stringConverter.apply(this.data); - } + return text; } @Override @@ -132,33 +130,14 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { return this; } - @Override - public Insets getInsets() { - return insets; - } - - /** - * Sets the insets for the text within the cell - * - * @param insets The insets. - * - * @return As a utility, returns this. - */ - public DefaultCellModel setInsets(Insets insets) { - this.insets = insets; - return this; - } - @Override public List getPopupMenu() { if (popupMenu != null) { return Collections.unmodifiableList(popupMenu); } - if (menuItemSupplier != null) { return this.menuItemSupplier.get(); } - return null; } @@ -166,6 +145,7 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { * Sets a function to lazy load the popup menu items. * * @param menuItemSupplier The lazy load function for popup items. + * * @return */ public DefaultCellModel setPopupMenuRetriever(Supplier> menuItemSupplier) { @@ -177,6 +157,7 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { * Sets the list of items for a popup menu * * @param popupMenu + * * @return As a utility, returns this. */ public DefaultCellModel setPopupMenu(List popupMenu) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java index e1c7fa0944..bac74d8fd5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; -import java.awt.Insets; import java.util.List; /** @@ -72,11 +71,6 @@ public interface GuiCellModel extends CellModel { } } - /** - * @return The insets for the cell text. - */ - Insets getInsets(); - /** * @return The popup menu associated with this cell or null if no popup menu * should be shown for this cell. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index be814d23a9..b1713d366a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -41,6 +41,8 @@ import javax.swing.table.TableColumnModel; */ public class JTablePanel extends AbstractLoadableComponent> { + private static final int EXTRA_ROW_HEIGHT = 4; + /** * An event that wraps a swing MouseEvent also providing context within the * table cell. @@ -56,10 +58,10 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * Main constructor. * - * @param e The underlying mouse event. - * @param table The table that was the target of the mouse event. - * @param row The row within the table that the event occurs. - * @param col The column within the table that the event occurs. + * @param e The underlying mouse event. + * @param table The table that was the target of the mouse event. + * @param row The row within the table that the event occurs. + * @param col The column within the table that the event occurs. * @param cellValue The value within the cell. */ public CellMouseEvent(MouseEvent e, JTable table, int row, int col, Object cellValue) { @@ -115,15 +117,14 @@ public class JTablePanel extends AbstractLoadableComponent> { * Handles mouse events at a cell level for the table. * * @param e The event containing information about the cell, the mouse - * event, and the table. + * event, and the table. */ void mouseClicked(CellMouseEvent e); } /** - * JTables don't allow displaying messages. So this LayerUI is used to - * display the contents of a child JLabel. Inspired by TableWaitLayerTest - * (Animating a Busy Indicator): + * This LayerUI is used to display the contents of a child JLabel. Inspired + * by TableWaitLayerTest (Animating a Busy Indicator): * https://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html. */ private static class Overlay extends LayerUI { @@ -205,7 +206,7 @@ public class JTablePanel extends AbstractLoadableComponent> { .map((colModel) -> colModel.getCellRenderer()) .collect(Collectors.toList()); - return new DefaultListTableModel(columnRenderers); + return new DefaultListTableModel<>(columnRenderers); } /** @@ -225,7 +226,6 @@ public class JTablePanel extends AbstractLoadableComponent> { return resultTable; } - private JScrollPane tableScrollPane; private Overlay overlayLayer; private ListTableModel tableModel; @@ -241,6 +241,7 @@ public class JTablePanel extends AbstractLoadableComponent> { public JTablePanel(ListTableModel tableModel) { this(); setModel(tableModel); + table.setRowHeight(table.getRowHeight() + EXTRA_ROW_HEIGHT); } /** @@ -268,6 +269,7 @@ public class JTablePanel extends AbstractLoadableComponent> { } } }); + table.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor")); } /** @@ -290,7 +292,7 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * @return The current listener for mouse events. The events provided to - * this listener will have cell and table context. + * this listener will have cell and table context. */ public CellMouseListener getCellListener() { return cellListener; @@ -300,7 +302,8 @@ public class JTablePanel extends AbstractLoadableComponent> { * Sets the current listener for mouse events. * * @param cellListener The event listener that will receive these events - * with cell and table context. + * with cell and table context. + * * @return */ public JTablePanel setCellListener(CellMouseListener cellListener) { @@ -329,7 +332,8 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * @return The function for determining the key for a data row. This key is - * used to maintain current selection in the table despite changing rows. + * used to maintain current selection in the table despite changing + * rows. */ public Function getKeyFunction() { return keyFunction; @@ -351,9 +355,10 @@ public class JTablePanel extends AbstractLoadableComponent> { this.keyFunction = keyFunction; return this; } - + /** * Returns the selected items or null if no item is selected. + * * @return The selected items or null if no item is selected. */ public List getSelectedItems() { From 7a0dc22ba24562006af87fc7b3864593209f1f10 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 28 May 2021 17:13:27 -0400 Subject: [PATCH 76/78] 7660 update copyright dates --- .../org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java | 2 +- .../autopsy/datasourcesummary/ui/DataSourceBrowser.java | 2 +- .../autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java | 2 +- .../datasourcesummary/uiutils/CellModelTableCellRenderer.java | 2 +- .../autopsy/datasourcesummary/uiutils/JTablePanel.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index c0afb624e7..f2b2a235f8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016-2019 Basis Technology Corp. + * Copyright 2016-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java index bb945833aa..d7ffe42980 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java index 634bb159cc..597cd01682 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 1f18e23e30..cad654e40e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index b1713d366a..89cf39eb0b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); From 301ca6112650e926a0128bf6821f98d25be0ec7a Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 28 May 2021 17:36:10 -0400 Subject: [PATCH 77/78] 7660 fix ingest history tables --- .../sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form | 6 ++++++ .../sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form index 4e74529073..4ec9df679e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.form @@ -78,6 +78,9 @@ + + + @@ -153,6 +156,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index f2b2a235f8..5fe65783b0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -50,7 +50,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE); - + private static final int EXTRA_ROW_HEIGHT = 4; private List ingestJobs; private final List ingestJobsForSelectedDataSource = new ArrayList<>(); private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); @@ -100,6 +100,9 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { } } }); + ingestJobTable.setRowHeight(ingestJobTable.getRowHeight() + EXTRA_ROW_HEIGHT); + ingestModuleTable.setRowHeight(ingestModuleTable.getRowHeight() + EXTRA_ROW_HEIGHT); + } /** @@ -278,6 +281,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { ingestJobTable.setModel(ingestJobTableModel); ingestJobTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor")); + ingestJobTable.setIntercellSpacing(new java.awt.Dimension(4, 2)); ingestJobTable.getTableHeader().setReorderingAllowed(false); ingestJobsScrollPane.setViewportView(ingestJobTable); ingestJobTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); @@ -314,6 +318,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { ingestModuleTable.setModel(ingestModuleTableModel); ingestModuleTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor")); + ingestModuleTable.setIntercellSpacing(new java.awt.Dimension(4, 2)); ingestModulesScrollPane.setViewportView(ingestModuleTable); gridBagConstraints = new java.awt.GridBagConstraints(); From ae0d95b0899b3d49f59d12c47080ea4cc0f2f2d5 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sun, 30 May 2021 10:12:21 -0400 Subject: [PATCH 78/78] Update ExtractRegistry.java Check length of Tokens after split, if they are not greater than 2 then skip trying to parse the token. --- .../recentactivity/ExtractRegistry.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index fd0c24e091..cd2eefc4f4 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1434,21 +1434,23 @@ class ExtractRegistry extends Extract { && !line.contains(("Recent File List"))) { // Split line on "> " which is the record delimiter between position and file String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - try{ - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (tokens.length > 1) { + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + try{ + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); if (bba != null) { bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } } + } catch(TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex); } - } catch(TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create TSK_RECENT_OBJECT artifact for file %d", regFile.getId()), ex); } line = reader.readLine(); }