diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java index 9b4f610721..a6af74ad3c 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java @@ -53,7 +53,7 @@ public interface CentralRepository { case SQLITE: return SqliteCentralRepo.getInstance(); default: - throw new CentralRepoException("Failed to get CentralRepository instance, Central Repositiory is not enabled."); + return null; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java index e0dcf11361..efd8b76152 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java @@ -121,18 +121,22 @@ public class PersonaAccount { /** * Creates an account for the specified Persona. * - * @param persona Persona for which the account is being added. - * @param account Account. + * @param persona Persona for which the account is being added. + * @param account Account. * @param justification Reason for assigning the alias, may be null. - * @param confidence Confidence level. + * @param confidence Confidence level. * * @return PersonaAccount - * * @throws CentralRepoException If there is an error in creating the - * account. + * account. */ static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException { CentralRepository cr = CentralRepository.getInstance(); + + if(cr == null) { + throw new CentralRepoException("Failed to add Persona, Central Repository is not enable"); + } + CentralRepoExaminer currentExaminer = cr.getOrInsertExaminer(System.getProperty("user.name")); Instant instant = Instant.now(); @@ -147,7 +151,7 @@ public class PersonaAccount { + currentExaminer.getId() + ")"; - cr.executeInsertSQL(insertClause); + CentralRepository.getInstance().executeInsertSQL(insertClause); String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE + "WHERE persona_id = " + persona.getId() @@ -245,13 +249,19 @@ public class PersonaAccount { * persona_account. */ static Collection getPersonaAccountsForPersona(long personaId) throws CentralRepoException { - String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE - + " WHERE persona_accounts.persona_id = " + personaId; + CentralRepository cr = CentralRepository.getInstance(); - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + if (cr != null) { + String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE + + " WHERE persona_accounts.persona_id = " + personaId; - return queryCallback.getPersonaAccountsList(); + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } /** @@ -269,10 +279,16 @@ public class PersonaAccount { + " WHERE persona_accounts.account_id = " + accountId + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + CentralRepository cr = CentralRepository.getInstance(); - return queryCallback.getPersonaAccountsList(); + if (cr != null) { + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } /** @@ -292,10 +308,15 @@ public class PersonaAccount { + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')" + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + CentralRepository cr = CentralRepository.getInstance(); + if (cr != null) { + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); - return queryCallback.getPersonaAccountsList(); + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } /** @@ -314,10 +335,14 @@ public class PersonaAccount { + " AND type_name = '" + account.getAccountType().getTypeName() + "' " + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); - return queryCallback.getPersonaAccountsList(); + CentralRepository cr = CentralRepository.getInstance(); + if (cr != null) { + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + return queryCallback.getPersonaAccountsList(); + } + return new ArrayList<>(); } /** @@ -326,24 +351,36 @@ public class PersonaAccount { * @param id row id for the account to be removed * * @throws CentralRepoException If there is an error in removing the - * account. + * account. */ static void removePersonaAccount(long id) throws CentralRepoException { + CentralRepository cr = CentralRepository.getInstance(); + + if(cr == null) { + throw new CentralRepoException("Failed to remove persona account, Central Repo is not enabled"); + } + String deleteClause = " DELETE FROM persona_accounts WHERE id = " + id; - CentralRepository.getInstance().executeDeleteSQL(deleteClause); + cr.executeDeleteSQL(deleteClause); } - + /** * Modifies the PersonaAccount row by the given id * * @param id row id for the account to be removed * * @throws CentralRepoException If there is an error in removing the - * account. + * account. */ static void modifyPersonaAccount(long id, Persona.Confidence confidence, String justification) throws CentralRepoException { + CentralRepository cr = CentralRepository.getInstance(); + + if (cr == null) { + throw new CentralRepoException("Failed to modify persona account, Central Repo is not enabled"); + } + String updateClause = "UPDATE persona_accounts SET confidence_id = " + confidence.getLevelId() + ", justification = \"" + justification + "\" WHERE id = " + id; - CentralRepository.getInstance().executeUpdateSQL(updateClause); + cr.executeUpdateSQL(updateClause); } /** @@ -381,25 +418,28 @@ public class PersonaAccount { * @param personaId Id of the persona to look for. * * @return Collection of all accounts associated with the given persona, may - * be empty. - * + * be empty. * @throws CentralRepoException If there is an error in getting the - * accounts. + * accounts. */ static Collection getAccountsForPersona(long personaId) throws CentralRepoException { + CentralRepository cr = CentralRepository.getInstance(); - String queryClause = "SELECT account_id, " - + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," - + " account_types.type_name as type_name " - + " FROM persona_accounts " - + " JOIN accounts as accounts on persona_accounts.account_id = accounts.id " - + " JOIN account_types as account_types on accounts.account_type_id = account_types.id " - + " WHERE persona_accounts.persona_id = " + personaId; + if (cr != null) { + String queryClause = "SELECT account_id, " + + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," + + " account_types.type_name as type_name " + + " FROM persona_accounts " + + " JOIN accounts as accounts on persona_accounts.account_id = accounts.id " + + " JOIN account_types as account_types on accounts.account_type_id = account_types.id " + + " WHERE persona_accounts.persona_id = " + personaId; - AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); - return queryCallback.getAccountsList(); + return queryCallback.getAccountsList(); + } + return new ArrayList<>(); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index db7acfc739..8d9943ffc4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -63,7 +63,6 @@ import org.sleuthkit.datamodel.TskCoreException; public class ExtractedContent implements AutopsyVisitableItem { private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); - private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text"); private final long filteringDSObjId; // 0 if not filtering/grouping by data source private SleuthkitCase skCase; // set to null after case has been closed @@ -145,12 +144,19 @@ public class ExtractedContent implements AutopsyVisitableItem { * This area has all of the blackboard artifacts that are not displayed in a * more specific form elsewhere in the tree. */ - private class TypeFactory extends ChildFactory.Detachable { + private class TypeFactory extends ChildFactory.Detachable implements RefreshThrottler.Refresher { private final ArrayList doNotShow = new ArrayList<>(); // maps the artifact type to its child node private final HashMap typeNodeList = new HashMap<>(); + /** + * RefreshThrottler is used to limit the number of refreshes performed + * when CONTENT_CHANGED and DATA_ADDED ingest module events are + * received. + */ + private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); + @SuppressWarnings("deprecation") TypeFactory() { super(); @@ -173,27 +179,11 @@ public class ExtractedContent implements AutopsyVisitableItem { private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { - /** - * This is a stop gap measure until a different way of handling - * the closing of cases is worked out. Currently, remote events - * may be received for a case that is already closed. - */ - try { - Case.getCurrentCaseThrows(); - /** - * Due to some unresolved issues with how cases are closed, - * it is possible for the event to have a null oldValue if - * the event is a remote event. - */ - final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); - if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) { - refresh(true); - } - } catch (NoCurrentCaseException notUsed) { - /** - * Case is closed, do nothing. - */ + 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 + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { @@ -204,32 +194,26 @@ public class ExtractedContent implements AutopsyVisitableItem { */ try { Case.getCurrentCaseThrows(); - refresh(true); + refresh(false); } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ } - } 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 - if (evt.getNewValue() == null) { - removeNotify(); - skCase = null; - } } }; @Override protected void addNotify() { + refreshThrottler.registerForIngestModuleEvents(); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); - IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); } @Override protected void removeNotify() { + refreshThrottler.unregisterEventListener(); IngestManager.getInstance().removeIngestJobEventListener(pcl); - IngestManager.getInstance().removeIngestModuleEventListener(pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); typeNodeList.clear(); } @@ -273,6 +257,40 @@ public class ExtractedContent implements AutopsyVisitableItem { typeNodeList.put(key, node); return node; } + + @Override + public void refresh() { + refresh(false); + } + + @Override + public boolean isRefreshRequired(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + /** + * This is a stop gap measure until a different way of handling + * the closing of cases is worked out. Currently, remote events + * may be received for a case that is already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * Due to some unresolved issues with how cases are closed, + * it is possible for the event to have a null oldValue if + * the event is a remote event. + */ + final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); + if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) { + return true; + } + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } + return false; + } } /** @@ -355,73 +373,53 @@ public class ExtractedContent implements AutopsyVisitableItem { /** * Creates children for a given artifact type */ - private class ArtifactFactory extends BaseChildFactory { + private class ArtifactFactory extends BaseChildFactory implements RefreshThrottler.Refresher { - private BlackboardArtifact.Type type; + private final BlackboardArtifact.Type type; + + /** + * RefreshThrottler is used to limit the number of refreshes performed + * when CONTENT_CHANGED and DATA_ADDED ingest module events are + * received. + */ + private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); ArtifactFactory(BlackboardArtifact.Type type) { super(type.getTypeName()); this.type = type; } - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + refresh(false); + } catch (NoCurrentCaseException notUsed) { /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked - * out. Currently, remote events may be received for a case - * that is already closed. + * Case is closed, do nothing. */ - try { - Case.getCurrentCaseThrows(); - /** - * Even with the check above, it is still possible that - * the case will be closed in a different thread before - * this code executes. If that happens, it is possible - * for the event to have a null oldValue. - */ - final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); - if (null != event && event.getBlackboardArtifactType().equals(type)) { - refresh(true); - } - } catch (NoCurrentCaseException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked - * out. Currently, remote events may be received for a case - * that is already closed. - */ - try { - Case.getCurrentCaseThrows(); - refresh(true); - } catch (NoCurrentCaseException notUsed) { - /** - * Case is closed, do nothing. - */ - } } } }; @Override protected void onAdd() { + refreshThrottler.registerForIngestModuleEvents(); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); - IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); } @Override protected void onRemove() { + refreshThrottler.unregisterEventListener(); IngestManager.getInstance().removeIngestJobEventListener(pcl); - IngestManager.getInstance().removeIngestModuleEventListener(pcl); } @Override @@ -451,5 +449,43 @@ public class ExtractedContent implements AutopsyVisitableItem { } return Collections.emptyList(); } + + @Override + public void refresh() { + refresh(false); + } + + @Override + public boolean isRefreshRequired(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * Even with the check above, it is still possible that the + * case will be closed in a different thread before this + * code executes. If that happens, it is possible for the + * event to have a null oldValue. + */ + final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); + if (null != event && event.getBlackboardArtifactType().equals(type)) { + return true; + } + + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } + return false; + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 83b05019ca..566545eecd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -82,39 +82,26 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - private class FileTypesByExtObservable extends Observable { + private class FileTypesByExtObservable extends Observable implements RefreshThrottler.Refresher { private final PropertyChangeListener pcl; private final Set CASE_EVENTS_OF_INTEREST; + /** + * RefreshThrottler is used to limit the number of refreshes performed + * when CONTENT_CHANGED and DATA_ADDED ingest module events are + * received. + */ + private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); private FileTypesByExtObservable() { super(); this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); this.pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { - /** - * If a new file has been added but does not have an extension - * there is nothing to do. - */ - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { - if ((evt.getOldValue() instanceof ModuleContentEvent) == false) { - return; - } - ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue(); - if ((moduleContentEvent.getSource() instanceof AbstractFile) == false) { - return; - } - AbstractFile abstractFile = (AbstractFile) moduleContentEvent.getSource(); - if (abstractFile.getNameExtension().isEmpty()) { - return; - } - } - /** * Checking for a current case is a stop gap measure until a * different way of handling the closing of cases is worked @@ -139,14 +126,14 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { }; IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); - IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); + refreshThrottler.registerForIngestModuleEvents(); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); - IngestManager.getInstance().removeIngestModuleEventListener(pcl); + refreshThrottler.unregisterEventListener(); Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); } @@ -154,7 +141,52 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { setChanged(); notifyObservers(); } + + @Override + public void refresh() { + typesRoot.updateShowCounts(); + update(); + } + + @Override + public boolean isRefreshRequired(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * If a new file has been added but does not have an + * extension there is nothing to do. + */ + if ((evt.getOldValue() instanceof ModuleContentEvent) == false) { + return false; + } + ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue(); + if ((moduleContentEvent.getSource() instanceof AbstractFile) == false) { + return false; + } + AbstractFile abstractFile = (AbstractFile) moduleContentEvent.getSource(); + if (!abstractFile.getNameExtension().isEmpty()) { + return true; + } + } catch (NoCurrentCaseException ex) { + /** + * Case is closed, no refresh needed. + */ + return false; + } + } + return false; + } } + private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RefreshThrottler.java b/Core/src/org/sleuthkit/autopsy/datamodel/RefreshThrottler.java new file mode 100644 index 0000000000..a8d3ed5581 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RefreshThrottler.java @@ -0,0 +1,125 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datamodel; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.sleuthkit.autopsy.ingest.IngestManager; + +/** + * Utility class that can be used by UI nodes to reduce the number of + * potentially expensive UI refresh events when DATA_ADDED and CONTENT_CHANGED + * ingest manager events are received. + */ +class RefreshThrottler { + + /** + * The Refresher interface needs to be implemented by ChildFactory instances + * that wish to take advantage of throttled refresh functionality. + */ + interface Refresher { + + /** + * The RefreshThrottler calls this method when the RefreshTask runs. + * + */ + void refresh(); + + /** + * Determine whether the given event should result in a refresh. + * + * @param evt + * + * @return true if event should trigger a refresh, otherwise false. + */ + boolean isRefreshRequired(PropertyChangeEvent evt); + } + + static ScheduledThreadPoolExecutor refreshExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("Node Refresh Thread").build()); + // Keep a thread safe reference to the current refresh task (if any) + private final AtomicReference refreshTaskRef; + + // The factory instance that will be called when a refresh is due. + private final Refresher refresher; + + private static final long MIN_SECONDS_BETWEEN_REFRESH = 5; + + private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED, IngestManager.IngestModuleEvent.CONTENT_CHANGED); + + /** + * A RefreshTask is scheduled to run when an event arrives and there isn't + * one already scheduled. + */ + private final class RefreshTask implements Runnable { + + @Override + public void run() { + // Call refresh on the factory + refresher.refresh(); + // Clear the refresh task reference + refreshTaskRef.set(null); + } + } + + /** + * PropertyChangeListener that reacts to DATA_ADDED and CONTENT_CHANGED + * events and schedules a refresh task if one is not already scheduled. + */ + private final PropertyChangeListener pcl; + + RefreshThrottler(Refresher r) { + this.refreshTaskRef = new AtomicReference<>(null); + refresher = r; + + pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString()) + || eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + if (!refresher.isRefreshRequired(evt)) { + return; + } + + RefreshTask task = new RefreshTask(); + if (refreshTaskRef.compareAndSet(null, task)) { + refreshExecutor.schedule(task, MIN_SECONDS_BETWEEN_REFRESH, TimeUnit.SECONDS); + } + } + }; + } + + /** + * Set up listener for ingest module events of interest. + */ + void registerForIngestModuleEvents() { + IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); + } + + /** + * Remove ingest module event listener. + */ + void unregisterEventListener() { + IngestManager.getInstance().removeIngestModuleEventListener(pcl); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/discovery/AbstractFiltersPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/AbstractFiltersPanel.java index 7bcd1c8243..2f718c3aa5 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/AbstractFiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/AbstractFiltersPanel.java @@ -101,7 +101,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li constraints.weightx = LABEL_WEIGHT; constraints.weighty = LABEL_WEIGHT; constraints.gridwidth = LABEL_WIDTH; - addToGridBagLayout(filterPanel.getCheckbox(), null, column); + addToGridBagLayout(filterPanel.getCheckbox(), filterPanel.getAdditionalLabel(), column); if (filterPanel.hasPanel()) { constraints.gridx += constraints.gridwidth; constraints.fill = GridBagConstraints.BOTH; diff --git a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties index 52c989c8e7..795018f1ac 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties @@ -101,3 +101,6 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show DiscoveryDialog.step1Label.text=Step 1: Choose result type +ResultsSplitPaneDivider.hideButton.text= +ResultsSplitPaneDivider.showButton.text= +ResultsSplitPaneDivider.detailsLabel.text=Details Area diff --git a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED index 0779798a6e..df1b6b8fd2 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED @@ -11,7 +11,7 @@ DiscoveryTopComponent.name=\ Discovery DiscoveryTopComponent.newSearch.text=New Search DiscoveryTopComponent.searchCancelled.text=Search has been cancelled. # {0} - search -DiscoveryTopComponent.searchComplete.text=Results for {0} +DiscoveryTopComponent.searchComplete.text=Results with {0} # {0} - searchType DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait. DiscoveryUiUtility.bytes.text=bytes @@ -57,18 +57,24 @@ FileSearch.InterestingItemGroupKey.noSets=None FileSearch.KeywordListGroupKey.noKeywords=None FileSearch.NoGroupingGroupKey.allFiles=All Files FileSearch.ObjectDetectedGroupKey.noSets=None -FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB -FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB -FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB -FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB -FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB -FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB -FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB -FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB -FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB -FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB -FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+ -FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+ +FileSearchData.FileSize.100kbto1mb=: 100KB-1MB +FileSearchData.FileSize.100mbto1gb=: 100MB-1GB +FileSearchData.FileSize.10PlusGb=: 10GB+ +FileSearchData.FileSize.16kbto100kb=: 16-100KB +FileSearchData.FileSize.1gbto5gb=: 1-5GB +FileSearchData.FileSize.1mbto50mb=: 1-50MB +FileSearchData.FileSize.200PlusMb=: 200MB+ +FileSearchData.FileSize.500kbto100mb=: 500KB-100MB +FileSearchData.FileSize.50mbto200mb=: 50-200MB +FileSearchData.FileSize.5gbto10gb=: 5-10GB +FileSearchData.FileSize.LARGE.displayName=Large +FileSearchData.FileSize.MEDIUM.displayName=Medium +FileSearchData.FileSize.SMALL.displayName=Small +FileSearchData.FileSize.upTo16kb=: 0-16KB +FileSearchData.FileSize.upTo500kb=: 0-500KB +FileSearchData.FileSize.XLARGE.displayName=XLarge +FileSearchData.FileSize.XSMALL.displayName=XSmall +FileSearchData.FileSize.XXLARGE.displayName=XXLarge FileSearchData.FileType.Audio.displayName=Audio FileSearchData.FileType.Documents.displayName=Documents FileSearchData.FileType.Executables.displayName=Executables @@ -106,45 +112,44 @@ FileSearchFiltering.concatenateSetNamesForDisplay.comma=, # {1} - Data source ID FileSearchFiltering.DataSourceFilter.datasource={0}({1}) # {0} - filters -FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0} -FileSearchFiltering.DataSourceFilter.or=\ or +FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0} +FileSearchFiltering.DataSourceFilter.or=, # {0} - filters -FileSearchFiltering.FileTypeFilter.desc=Files with type: {0} -FileSearchFiltering.FileTypeFilter.or=\ or +FileSearchFiltering.FileTypeFilter.desc=Type: {0} +FileSearchFiltering.FileTypeFilter.or=, # {0} - filters -FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0} -FileSearchFiltering.FrequencyFilter.or=\ or +FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0} +FileSearchFiltering.FrequencyFilter.or=, # {0} - filters -FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0} +FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0} # {0} - filters -FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0} +FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0} # {0} - filters -FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} -FileSearchFiltering.KnownFilter.desc=Files which are not known +FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0} +FileSearchFiltering.KnownFilter.desc=which are not known # {0} - filters -FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0} +FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0} # {0} - filters -FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} +FileSearchFiltering.ParentFilter.desc=Paths matching: {0} FileSearchFiltering.ParentFilter.exact=(exact match) -FileSearchFiltering.ParentFilter.or=\ or +FileSearchFiltering.ParentFilter.excluded=(excluded) +FileSearchFiltering.ParentFilter.included=(included) +FileSearchFiltering.ParentFilter.or=, FileSearchFiltering.ParentFilter.substring=(substring) FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude) FileSearchFiltering.ParentSearchTerm.fullString=\ (exact) FileSearchFiltering.ParentSearchTerm.includeString=\ (include) FileSearchFiltering.ParentSearchTerm.subString=\ (substring) -FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable +FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable # {0} - filters -FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0} +FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0} # {0} - filters -FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} -FileSearchFiltering.SizeFilter.or=\ or -# {0} - Minimum bytes -# {1} - Maximum bytes -FileSearchFiltering.SizeFilter.range=({0} to {1}) +FileSearchFiltering.SizeFilter.desc=Size(s): {0} +FileSearchFiltering.SizeFilter.or=, # {0} - tag names -FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0} -FileSearchFiltering.TagsFilter.or=\ or -FileSearchFiltering.UserCreatedFilter.desc=Files that contain EXIF data +FileSearchFiltering.TagsFilter.desc=Tagged {0} +FileSearchFiltering.TagsFilter.or=, +FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data FileSearchPanel.sortingPanel.border.title=Grouping FileSearchPanel.addButton.text=Add FileSearchPanel.substringRadioButton.text=Substring @@ -259,6 +264,9 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show DiscoveryDialog.step1Label.text=Step 1: Choose result type +ResultsSplitPaneDivider.hideButton.text= +ResultsSplitPaneDivider.showButton.text= +ResultsSplitPaneDivider.detailsLabel.text=Details Area VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.gigaBytes.text=GB diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form index d19a7dda96..19116a24fe 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form @@ -43,23 +43,25 @@ - - - - - - - - + + + + + + + + + + + - diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java index 948dbd4bee..33f69712f0 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java @@ -275,19 +275,21 @@ final class DiscoveryDialog extends javax.swing.JDialog { toolBarPanelLayout.setHorizontalGroup( toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(toolBarPanelLayout.createSequentialGroup() - .addContainerGap(196, Short.MAX_VALUE) - .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, toolBarPanelLayout.createSequentialGroup() - .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(step1Label, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap() + .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(toolBarPanelLayout.createSequentialGroup() + .addGap(10, 10, 10) .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(documentsButton))) - .addContainerGap(196, Short.MAX_VALUE)) + .addComponent(documentsButton) + .addContainerGap(370, Short.MAX_VALUE)) + .addGroup(toolBarPanelLayout.createSequentialGroup() + .addComponent(step1Label, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(391, Short.MAX_VALUE)))) ); toolBarPanelLayout.setVerticalGroup( toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form index 4e62b44cb9..4a1f471190 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form @@ -41,7 +41,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.java b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.java index 30341184ad..607459325d 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.java @@ -19,11 +19,14 @@ package org.sleuthkit.autopsy.discovery; import com.google.common.eventbus.Subscribe; +import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.util.List; import java.util.stream.Collectors; import javax.swing.JSplitPane; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.windows.Mode; @@ -67,6 +70,34 @@ public final class DiscoveryTopComponent extends TopComponent { mainSplitPane.setLeftComponent(groupListPanel); rightSplitPane.setTopComponent(resultsPanel); rightSplitPane.setBottomComponent(detailsPanel); + //set color of divider + rightSplitPane.setUI(new BasicSplitPaneUI() { + @Override + public BasicSplitPaneDivider createDefaultDivider() { + return new BasicSplitPaneDividerImpl(this); + + } + }); + } + + /** + * Private class for replacing the divider for the results split pane. + */ + private final class BasicSplitPaneDividerImpl extends BasicSplitPaneDivider { + + /** + * Construct a new BasicSplitPaneDividerImpl. + * + * @param ui The component which contains the split pane this divider is + * in. + */ + BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) { + super(ui); + this.setLayout(new BorderLayout()); + this.add(new ResultsSplitPaneDivider()); + } + + private static final long serialVersionUID = 1L; } /** @@ -129,7 +160,7 @@ public final class DiscoveryTopComponent extends TopComponent { mainSplitPane.setDividerLocation(250); mainSplitPane.setPreferredSize(new java.awt.Dimension(1100, 700)); - rightSplitPane.setDividerSize(15); + rightSplitPane.setDividerSize(35); rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); rightSplitPane.setResizeWeight(1.0); rightSplitPane.setPreferredSize(new java.awt.Dimension(800, 700)); @@ -249,11 +280,11 @@ public final class DiscoveryTopComponent extends TopComponent { @Subscribe @Messages({"DiscoveryTopComponent.newSearch.text=New Search", "# {0} - search", - "DiscoveryTopComponent.searchComplete.text=Results for {0}"}) + "DiscoveryTopComponent.searchComplete.text=Results with {0}"}) void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) { newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); progressMessageTextArea.setForeground(Color.black); - progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(searchCompleteEvent.getFilters().stream().map(FileFilter::getDesc).collect(Collectors.joining(", ")))); + progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(searchCompleteEvent.getFilters().stream().map(FileFilter::getDesc).collect(Collectors.joining("; ")))); progressMessageTextArea.setCaretPosition(0); } @@ -269,6 +300,7 @@ public final class DiscoveryTopComponent extends TopComponent { newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); progressMessageTextArea.setForeground(Color.red); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text()); + } /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DocumentFilterPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/DocumentFilterPanel.java index 9756045387..216a3c12b3 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/DocumentFilterPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/DocumentFilterPanel.java @@ -45,7 +45,6 @@ final class DocumentFilterPanel extends AbstractFiltersPanel { addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0); addFilter(new HashSetFilterPanel(), false, null, 1); addFilter(new InterestingItemsFilterPanel(), false, null, 1); - addFilter(new ObjectDetectedFilterPanel(), false, null, 1); addFilter(new ParentFolderFilterPanel(), false, null, 1); addPanelsToScrollPane(documentsFiltersSplitPane); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/discovery/FileSearchData.java index 40df3ee905..81bb2ca641 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/FileSearchData.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/FileSearchData.java @@ -126,39 +126,46 @@ final class FileSearchData { * Enum representing the file size */ @NbBundle.Messages({ - "FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+", - "FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB", - "FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB", - "FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB", - "FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB", - "FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB", - "FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+", - "FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB", - "FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB", - "FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB", - "FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB", - "FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB",}) + "FileSearchData.FileSize.XXLARGE.displayName=XXLarge", + "FileSearchData.FileSize.XLARGE.displayName=XLarge", + "FileSearchData.FileSize.LARGE.displayName=Large", + "FileSearchData.FileSize.MEDIUM.displayName=Medium", + "FileSearchData.FileSize.SMALL.displayName=Small", + "FileSearchData.FileSize.XSMALL.displayName=XSmall", + "FileSearchData.FileSize.10PlusGb=: 10GB+", + "FileSearchData.FileSize.5gbto10gb=: 5-10GB", + "FileSearchData.FileSize.1gbto5gb=: 1-5GB", + "FileSearchData.FileSize.100mbto1gb=: 100MB-1GB", + "FileSearchData.FileSize.200PlusMb=: 200MB+", + "FileSearchData.FileSize.50mbto200mb=: 50-200MB", + "FileSearchData.FileSize.500kbto100mb=: 500KB-100MB", + "FileSearchData.FileSize.1mbto50mb=: 1-50MB", + "FileSearchData.FileSize.100kbto1mb=: 100KB-1MB", + "FileSearchData.FileSize.16kbto100kb=: 16-100KB", + "FileSearchData.FileSize.upTo500kb=: 0-500KB", + "FileSearchData.FileSize.upTo16kb=: 0-16KB",}) enum FileSize { - XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_VIDEO_displayName()), - XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_VIDEO_displayName()), - LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_VIDEO_displayName()), - MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_VIDEO_displayName()), - SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_VIDEO_displayName()), - XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_VIDEO_displayName()), - XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_IMAGE_displayName()), - XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_IMAGE_displayName()), - LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_IMAGE_displayName()), - MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_IMAGE_displayName()), - SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_IMAGE_displayName()), - XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_IMAGE_displayName()); + XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_10PlusGb()), + XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_5gbto10gb()), + LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1gbto5gb()), + MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100mbto1gb()), + SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_500kbto100mb()), + XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo500kb()), + XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_200PlusMb()), + XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_50mbto200mb()), + LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1mbto50mb()), + MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100kbto1mb()), + SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_16kbto100kb()), + XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo16kb()); private final int ranking; // Must be unique for each value private final long minBytes; // Note that the size must be strictly greater than this to match private final long maxBytes; - private final String displayName; + private final String sizeGroup; + private final String displaySize; final static long NO_MAXIMUM = -1; - FileSize(int ranking, long minB, long maxB, String displayName) { + FileSize(int ranking, long minB, long maxB, String displayName, String displaySize) { this.ranking = ranking; this.minBytes = minB; if (maxB >= 0) { @@ -166,7 +173,8 @@ final class FileSearchData { } else { this.maxBytes = NO_MAXIMUM; } - this.displayName = displayName; + this.sizeGroup = displayName; + this.displaySize = displaySize; } /** @@ -246,7 +254,11 @@ final class FileSearchData { @Override public String toString() { - return displayName; + return sizeGroup + displaySize; + } + + String getSizeGroup(){ + return sizeGroup; } /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/discovery/FileSearchFiltering.java index 326bc7c15c..adf301537a 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/FileSearchFiltering.java @@ -207,11 +207,8 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}", - "FileSearchFiltering.SizeFilter.or= or ", - "# {0} - Minimum bytes", - "# {1} - Maximum bytes", - "FileSearchFiltering.SizeFilter.range=({0} to {1})",}) + "FileSearchFiltering.SizeFilter.desc=Size(s): {0}", + "FileSearchFiltering.SizeFilter.or=, "}) @Override String getDesc() { String desc = ""; // NON-NLS @@ -219,7 +216,7 @@ class FileSearchFiltering { if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_SizeFilter_or(); } - desc += Bundle.FileSearchFiltering_SizeFilter_range(size.getMinBytes(), size.getMaxBytes()); + desc += size.getSizeGroup(); } desc = Bundle.FileSearchFiltering_SizeFilter_desc(desc); return desc; @@ -364,10 +361,12 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}", - "FileSearchFiltering.ParentFilter.or= or ", + "FileSearchFiltering.ParentFilter.desc=Paths matching: {0}", + "FileSearchFiltering.ParentFilter.or=, ", "FileSearchFiltering.ParentFilter.exact=(exact match)", - "FileSearchFiltering.ParentFilter.substring=(substring)",}) + "FileSearchFiltering.ParentFilter.substring=(substring)", + "FileSearchFiltering.ParentFilter.included=(included)", + "FileSearchFiltering.ParentFilter.excluded=(excluded)"}) @Override String getDesc() { String desc = ""; // NON-NLS @@ -380,6 +379,11 @@ class FileSearchFiltering { } else { desc += searchTerm.getSearchStr() + Bundle.FileSearchFiltering_ParentFilter_substring(); } + if (searchTerm.isIncluded()) { + desc += Bundle.FileSearchFiltering_ParentFilter_included(); + } else { + desc += Bundle.FileSearchFiltering_ParentFilter_excluded(); + } } desc = Bundle.FileSearchFiltering_ParentFilter_desc(desc); return desc; @@ -417,8 +421,8 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}", - "FileSearchFiltering.DataSourceFilter.or= or ", + "FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0}", + "FileSearchFiltering.DataSourceFilter.or=, ", "# {0} - Data source name", "# {1} - Data source ID", "FileSearchFiltering.DataSourceFilter.datasource={0}({1})",}) @@ -466,7 +470,7 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}",}) + "FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames)); @@ -516,8 +520,8 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}", - "FileSearchFiltering.FileTypeFilter.or= or ",}) + "FileSearchFiltering.FileTypeFilter.desc=Type: {0}", + "FileSearchFiltering.FileTypeFilter.or=, ",}) @Override String getDesc() { String desc = ""; @@ -586,8 +590,8 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}", - "FileSearchFiltering.FrequencyFilter.or= or ",}) + "FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0}", + "FileSearchFiltering.FrequencyFilter.or=, ",}) @Override String getDesc() { String desc = ""; // NON-NLS @@ -595,7 +599,7 @@ class FileSearchFiltering { if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_FrequencyFilter_or(); } - desc += freq.name(); + desc += freq.toString(); } return Bundle.FileSearchFiltering_FrequencyFilter_desc(desc); } @@ -632,7 +636,7 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0}",}) + "FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames)); @@ -670,7 +674,7 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0}",}) + "FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames)); @@ -708,7 +712,7 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0}",}) + "FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames)); @@ -784,7 +788,7 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}",}) + "FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_ScoreFilter_desc( @@ -826,8 +830,8 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - tag names", - "FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0}", - "FileSearchFiltering.TagsFilter.or= or ",}) + "FileSearchFiltering.TagsFilter.desc=Tagged {0}", + "FileSearchFiltering.TagsFilter.or=, ",}) @Override String getDesc() { String desc = ""; // NON-NLS @@ -862,7 +866,7 @@ class FileSearchFiltering { } @NbBundle.Messages({ - "FileSearchFiltering.UserCreatedFilter.desc=Files that contain EXIF data",}) + "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",}) @Override String getDesc() { return Bundle.FileSearchFiltering_UserCreatedFilter_desc(); @@ -931,7 +935,7 @@ class FileSearchFiltering { } @NbBundle.Messages({ - "FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable",}) + "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",}) @Override String getDesc() { return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); @@ -949,7 +953,7 @@ class FileSearchFiltering { } @NbBundle.Messages({ - "FileSearchFiltering.KnownFilter.desc=Files which are not known"}) + "FileSearchFiltering.KnownFilter.desc=which are not known"}) @Override String getDesc() { return Bundle.FileSearchFiltering_KnownFilter_desc(); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.form index c35bc16cb4..b350ab42b3 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.form @@ -37,6 +37,18 @@ + + + + + + + + + + + + @@ -143,6 +155,9 @@ + + + @@ -152,6 +167,9 @@ + + + @@ -161,6 +179,9 @@ + + + @@ -169,6 +190,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.java index 3ecb095868..7f10b38692 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ParentFolderFilterPanel.java @@ -66,6 +66,8 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel { parentCheckbox = new javax.swing.JCheckBox(); parentLabel = new javax.swing.JLabel(); + javax.swing.ButtonGroup includeButtonGroup = new javax.swing.ButtonGroup(); + javax.swing.ButtonGroup pathTypeButtonGroup = new javax.swing.ButtonGroup(); parentScrollPane = new javax.swing.JScrollPane(); parentList = new javax.swing.JList<>(); fullRadioButton = new javax.swing.JRadioButton(); @@ -106,17 +108,21 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel { }); parentScrollPane.setViewportView(parentList); + pathTypeButtonGroup.add(fullRadioButton); fullRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.fullRadioButton.text_1")); // NOI18N fullRadioButton.setEnabled(false); + includeButtonGroup.add(includeRadioButton); includeRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.includeRadioButton.text_1")); // NOI18N includeRadioButton.setEnabled(false); + pathTypeButtonGroup.add(substringRadioButton); org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.substringRadioButton.text_1")); // NOI18N substringRadioButton.setEnabled(false); + includeButtonGroup.add(excludeRadioButton); org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.excludeRadioButton.text_1")); // NOI18N excludeRadioButton.setEnabled(false); @@ -188,7 +194,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel { }// //GEN-END:initComponents private void parentCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parentCheckboxActionPerformed -// parentFilterSettings(true, true, parentCheckbox.isSelected(), null); + configurePanel(parentCheckbox.isSelected(), null); }//GEN-LAST:event_parentCheckboxActionPerformed private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged @@ -235,6 +241,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel { parentCheckbox.setSelected(selected); if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) { parentScrollPane.setEnabled(true); + parentLabel.setEnabled(true); includeRadioButton.setEnabled(true); excludeRadioButton.setEnabled(true); fullRadioButton.setEnabled(true); @@ -248,6 +255,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel { } } else { parentScrollPane.setEnabled(false); + parentLabel.setEnabled(false); parentList.setEnabled(false); includeRadioButton.setEnabled(false); excludeRadioButton.setEnabled(false); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.form b/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.form new file mode 100644 index 0000000000..48ae94d8a9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.form @@ -0,0 +1,141 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.java b/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.java new file mode 100644 index 0000000000..0a89f28711 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ResultsSplitPaneDivider.java @@ -0,0 +1,121 @@ +/* + * Autopsy + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.discovery; + +import java.awt.Cursor; + +/** + * Panel for separating the results list from the details area. + */ +final class ResultsSplitPaneDivider extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + /** + * Creates new form LabeledSplitPaneDivider. + */ + ResultsSplitPaneDivider() { + initComponents(); + } + + /** + * 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.JLabel detailsLabel = new javax.swing.JLabel(); + javax.swing.JButton hideButton = new javax.swing.JButton(); + javax.swing.JButton showButton = new javax.swing.JButton(); + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + + setBackground(new java.awt.Color(170, 170, 170)); + + org.openide.awt.Mnemonics.setLocalizedText(detailsLabel, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.detailsLabel.text")); // NOI18N + detailsLabel.setFocusable(false); + + hideButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/discovery/arrow-down.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hideButton, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.hideButton.text")); // NOI18N + hideButton.setBorder(null); + hideButton.setFocusable(false); + hideButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); + hideButton.setCursor(Cursor.getDefaultCursor()); + hideButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hideButtonActionPerformed(evt); + } + }); + + showButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/discovery/arrow-up.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(showButton, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.showButton.text")); // NOI18N + showButton.setBorder(null); + showButton.setFocusable(false); + showButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); + showButton.setCursor(Cursor.getDefaultCursor()); + showButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + showButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(detailsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 251, Short.MAX_VALUE) + .addComponent(showButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(hideButton) + .addContainerGap()) + .addComponent(filler2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(filler2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(0, 0, 0) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(hideButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(showButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, 0) + .addComponent(filler1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void showButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showButtonActionPerformed + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true)); + }//GEN-LAST:event_showButtonActionPerformed + + private void hideButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hideButtonActionPerformed + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false)); + }//GEN-LAST:event_hideButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.form index 259a188967..1a853b425a 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.form @@ -11,12 +11,17 @@ - + + + + + + @@ -32,7 +37,7 @@ - + @@ -45,7 +50,7 @@ - + @@ -54,7 +59,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.java index 48522a9474..4f2684339b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/VideoFilterPanel.java @@ -66,7 +66,9 @@ final class VideoFilterPanel extends AbstractFiltersPanel { setLayout(new java.awt.BorderLayout()); - videoFiltersPanel.setPreferredSize(new java.awt.Dimension(223, 66)); + videoFiltersScrollPane.setPreferredSize(new java.awt.Dimension(312, 102)); + + videoFiltersPanel.setPreferredSize(new java.awt.Dimension(310, 100)); videoFiltersSplitPane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(VideoFilterPanel.class, "VideoFilterPanel.videoFiltersSplitPane.border.title"))); // NOI18N videoFiltersSplitPane.setResizeWeight(0.5); @@ -77,14 +79,14 @@ final class VideoFilterPanel extends AbstractFiltersPanel { videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(videoFiltersPanelLayout.createSequentialGroup() .addGap(8, 8, 8) - .addComponent(videoFiltersSplitPane) + .addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 294, Short.MAX_VALUE) .addGap(8, 8, 8)) ); videoFiltersPanelLayout.setVerticalGroup( videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(videoFiltersPanelLayout.createSequentialGroup() .addGap(8, 8, 8) - .addComponent(videoFiltersSplitPane) + .addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 84, Short.MAX_VALUE) .addGap(8, 8, 8)) ); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/arrow-down.png b/Core/src/org/sleuthkit/autopsy/discovery/arrow-down.png new file mode 100644 index 0000000000..e934b25655 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/discovery/arrow-down.png differ diff --git a/Core/src/org/sleuthkit/autopsy/discovery/arrow-up.png b/Core/src/org/sleuthkit/autopsy/discovery/arrow-up.png new file mode 100644 index 0000000000..de7b88bbe2 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/discovery/arrow-up.png differ diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 80171f0672..7c3e52b81e 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1388,7 +1388,8 @@ class ExtractRegistry extends Extract { line = reader.readLine(); // Columns are // FileX -> - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets")) { + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets") + && !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]; diff --git a/thirdparty/rr/plugins/arunmru.pl b/thirdparty/rr/plugins/arunmru.pl index 504700f145..9d8ed281bd 100644 --- a/thirdparty/rr/plugins/arunmru.pl +++ b/thirdparty/rr/plugins/arunmru.pl @@ -36,43 +36,44 @@ sub pluginmain { my $class = shift; my $ntuser = shift; #::logMsg("autospyrunmru"); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; + if (defined(Parse::Win32Registry->new($ntuser))) { + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - #::rptMsg("RunMru"); - #::rptMsg($key_path); - - my @vals = $key->get_list_of_values(); - ::rptMsg(""); - ::rptMsg("".gmtime($key->get_timestamp()).""); - ::rptMsg(""); - my %runvals; - my $mru; - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - $runvals{$v->get_name()} = $v->get_data() unless ($v->get_name() =~ m/^MRUList/i); - $mru = $v->get_data() if ($v->get_name() =~ m/^MRUList/i); - } - ::rptMsg("".$mru.""); - foreach my $r (sort keys %runvals) { - ::rptMsg("".$r." ".$runvals{$r}.""); - } - } - else { - #::rptMsg($key_path." has no values."); - #::logMsg($key_path." has no values."); - } - ::rptMsg(""); - ::rptMsg(""); - } - else { - #::rptMsg($key_path." not found."); - #::logMsg($key_path." not found."); - } - + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + #::rptMsg("RunMru"); + #::rptMsg($key_path); + + my @vals = $key->get_list_of_values(); + ::rptMsg(""); + ::rptMsg("".gmtime($key->get_timestamp()).""); + ::rptMsg(""); + my %runvals; + my $mru; + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + $runvals{$v->get_name()} = $v->get_data() unless ($v->get_name() =~ m/^MRUList/i); + $mru = $v->get_data() if ($v->get_name() =~ m/^MRUList/i); + } + ::rptMsg("".$mru.""); + foreach my $r (sort keys %runvals) { + ::rptMsg("".$r." ".$runvals{$r}.""); + } + } + else { + #::rptMsg($key_path." has no values."); + #::logMsg($key_path." has no values."); + } + ::rptMsg(""); + ::rptMsg(""); + } + else { + #::rptMsg($key_path." not found."); + #::logMsg($key_path." not found."); + } + } } 1; diff --git a/thirdparty/rr/plugins/autopsylogin.pl b/thirdparty/rr/plugins/autopsylogin.pl index ab0365817e..2a72ba6936 100644 --- a/thirdparty/rr/plugins/autopsylogin.pl +++ b/thirdparty/rr/plugins/autopsylogin.pl @@ -35,36 +35,38 @@ sub pluginmain { my $class = shift; my $ntuser = shift; #::logMsg("||logonusername||"); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - my $logon_name = "Username"; - - my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - my @vals = $key->get_list_of_values(); - if (scalar(@vals) > 0) { - #::rptMsg("Logon User Name"); - #::rptMsg($key_path); - ::rptMsg(""); - ::rptMsg("".gmtime($key->get_timestamp()).""); - foreach my $v (@vals) { - if ($v->get_name() eq $logon_name) { - ::rptMsg(" ".$v->get_data() .""); - } - } - ::rptMsg(""); - } - else { - #::rptMsg($key_path." has no values."); - #::logMsg($key_path." has no values."); - } - } - else { - #::rptMsg($key_path." not found."); - #::logMsg($key_path." not found."); - } + if (defined(Parse::Win32Registry->new($ntuser))) { + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + my $logon_name = "Username"; + + my $key_path = 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + my @vals = $key->get_list_of_values(); + if (scalar(@vals) > 0) { + #::rptMsg("Logon User Name"); + #::rptMsg($key_path); + ::rptMsg(""); + ::rptMsg("".gmtime($key->get_timestamp()).""); + foreach my $v (@vals) { + if ($v->get_name() eq $logon_name) { + ::rptMsg(" ".$v->get_data() .""); + } + } + ::rptMsg(""); + } + else { + #::rptMsg($key_path." has no values."); + #::logMsg($key_path." has no values."); + } + } + else { + #::rptMsg($key_path." not found."); + #::logMsg($key_path." not found."); + } + } } 1; diff --git a/thirdparty/rr/plugins/autopsyntusernetwork.pl b/thirdparty/rr/plugins/autopsyntusernetwork.pl index 715e89b8ff..910679be95 100644 --- a/thirdparty/rr/plugins/autopsyntusernetwork.pl +++ b/thirdparty/rr/plugins/autopsyntusernetwork.pl @@ -30,64 +30,67 @@ sub pluginmain { my $ntuser = shift; #::logMsg("Launching ntusernetwork v.".$VERSION); #::rptMsg("ntusernetwork v.".$VERSION); # banner - #::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - - ::rptMsg(""); - ::rptMsg(""); - ::rptMsg(""); + #::rptMsg("(".$config{hive}.") ".getShortDescr()."\n"); # banner + if (defined(Parse::Win32Registry->new($ntuser))) { + + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + + ::rptMsg(""); + ::rptMsg(""); + ::rptMsg(""); - my $key_path = 'Network'; - my $key; - if ($key = $root_key->get_subkey($key_path)) { + my $key_path = 'Network'; + my $key; + if ($key = $root_key->get_subkey($key_path)) { - my @subkeys = $key->get_list_of_subkeys(); - if (scalar @subkeys > 0) { - foreach my $s (@subkeys) { - #::rptMsg($key_path."\\".$s->get_name()); - my $localPath = $key_path."\\".$s->get_name(); + my @subkeys = $key->get_list_of_subkeys(); + if (scalar @subkeys > 0) { + foreach my $s (@subkeys) { + #::rptMsg($key_path."\\".$s->get_name()); + my $localPath = $key_path."\\".$s->get_name(); - my $remotePath; - eval { - $remotePath = $s->get_value("RemotePath")->get_data(); - }; - if ($@) { - # ::rptMsg("OS value not found."); - } - else { - ::rptMsg("". $remotePath . ""); - } - } - } + my $remotePath; + eval { + $remotePath = $s->get_value("RemotePath")->get_data(); + }; + if ($@) { + # ::rptMsg("OS value not found."); + } + else { + ::rptMsg("". $remotePath . ""); + } + } + } - # ::rptMsg($key_path); - # ::rptMsg(""); + # ::rptMsg($key_path); + # ::rptMsg(""); - - # my @subkeys = $key->get_list_of_subkeys(); - # if (scalar @subkeys > 0) { - # foreach my $s (@subkeys) { - # ::rptMsg($key_path."\\".$s->get_name()); - # ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())); - # my @vals = $s->get_list_of_values(); - # if (scalar @vals > 0) { - # foreach my $v (@vals) { - # ::rptMsg(sprintf " %-15s %-25s",$v->get_name(),$v->get_data()); - # } - # ::rptMsg(""); - # } - # } - # } - # else { - # ::rptMsg($key_path." key has no subkeys."); - # } - } - else { - #::rptMsg($key_path." key not found."); - } - ::rptMsg(""); + + # my @subkeys = $key->get_list_of_subkeys(); + # if (scalar @subkeys > 0) { + # foreach my $s (@subkeys) { + # ::rptMsg($key_path."\\".$s->get_name()); + # ::rptMsg("LastWrite time: ".gmtime($s->get_timestamp())); + # my @vals = $s->get_list_of_values(); + # if (scalar @vals > 0) { + # foreach my $v (@vals) { + # ::rptMsg(sprintf " %-15s %-25s",$v->get_name(),$v->get_data()); + # } + # ::rptMsg(""); + # } + # } + # } + # else { + # ::rptMsg($key_path." key has no subkeys."); + # } + } + else { + #::rptMsg($key_path." key not found."); + } + ::rptMsg(""); + } } 1; diff --git a/thirdparty/rr/plugins/autopsyrecentdocs.pl b/thirdparty/rr/plugins/autopsyrecentdocs.pl index 776126175b..e2a05aa699 100644 --- a/thirdparty/rr/plugins/autopsyrecentdocs.pl +++ b/thirdparty/rr/plugins/autopsyrecentdocs.pl @@ -41,80 +41,82 @@ sub pluginmain { my $class = shift; my $ntuser = shift; #::logMsg("||recentdocs||"); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - #::rptMsg("RecentDocs"); - #::rptMsg("**All values printed in MRUList\\MRUListEx order."); - #::rptMsg($key_path); - ::rptMsg("".gmtime($key->get_timestamp()).""); -# Get RecentDocs values - my %rdvals = getRDValues($key); - if (%rdvals) { - my $tag; - if (exists $rdvals{"MRUListEx"}) { - $tag = "MRUListEx"; - } - elsif (exists $rdvals{"MRUList"}) { - $tag = "MRUList"; - } - else { - - } - - my @list = split(/,/,$rdvals{$tag}); - foreach my $i (@list) { - ::rptMsg("".$rdvals{$i} . ""); - } - - } - else { - #::rptMsg($key_path." has no values."); - #::logMsg("Error: ".$key_path." has no values."); - } - ::rptMsg(""); -# Get RecentDocs subkeys' values - my @subkeys = $key->get_list_of_subkeys(); - if (scalar(@subkeys) > 0) { - foreach my $s (@subkeys) { - #::rptMsg($key_path."\\".$s->get_name()); - #::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); - - my %rdvals = getRDValues($s); - if (%rdvals) { - my $tag; - if (exists $rdvals{"MRUListEx"}) { - $tag = "MRUListEx"; - } - elsif (exists $rdvals{"MRUList"}) { - $tag = "MRUList"; - } - else { - - } - - my @list = split(/,/,$rdvals{$tag}); - #::rptMsg($tag." = ".$rdvals{$tag}); - foreach my $i (@list) { - #::rptMsg("".$rdvals{$i}); - } - - #::rptMsg(""); - } - else { - #::rptMsg($key_path." has no values."); - } - } - } - else { - #::rptMsg($key_path." has no subkeys."); - } - } - else { - #::rptMsg($key_path." not found."); - } + if (defined(Parse::Win32Registry->new($ntuser))) { + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + #::rptMsg("RecentDocs"); + #::rptMsg("**All values printed in MRUList\\MRUListEx order."); + #::rptMsg($key_path); + ::rptMsg("".gmtime($key->get_timestamp()).""); + # Get RecentDocs values + my %rdvals = getRDValues($key); + if (%rdvals) { + my $tag; + if (exists $rdvals{"MRUListEx"}) { + $tag = "MRUListEx"; + } + elsif (exists $rdvals{"MRUList"}) { + $tag = "MRUList"; + } + else { + + } + + my @list = split(/,/,$rdvals{$tag}); + foreach my $i (@list) { + ::rptMsg("".$rdvals{$i} . ""); + } + + } + else { + #::rptMsg($key_path." has no values."); + #::logMsg("Error: ".$key_path." has no values."); + } + ::rptMsg(""); + # Get RecentDocs subkeys' values + my @subkeys = $key->get_list_of_subkeys(); + if (scalar(@subkeys) > 0) { + foreach my $s (@subkeys) { + #::rptMsg($key_path."\\".$s->get_name()); + #::rptMsg("LastWrite Time ".gmtime($s->get_timestamp())." (UTC)"); + + my %rdvals = getRDValues($s); + if (%rdvals) { + my $tag; + if (exists $rdvals{"MRUListEx"}) { + $tag = "MRUListEx"; + } + elsif (exists $rdvals{"MRUList"}) { + $tag = "MRUList"; + } + else { + + } + + my @list = split(/,/,$rdvals{$tag}); + #::rptMsg($tag." = ".$rdvals{$tag}); + foreach my $i (@list) { + #::rptMsg("".$rdvals{$i}); + } + + #::rptMsg(""); + } + else { + #::rptMsg($key_path." has no values."); + } + } + } + else { + #::rptMsg($key_path." has no subkeys."); + } + } + else { + #::rptMsg($key_path." not found."); + } + } } diff --git a/thirdparty/rr/plugins/autopsyshellfolders.pl b/thirdparty/rr/plugins/autopsyshellfolders.pl index d625820ec5..01a5b22e6a 100644 --- a/thirdparty/rr/plugins/autopsyshellfolders.pl +++ b/thirdparty/rr/plugins/autopsyshellfolders.pl @@ -41,32 +41,35 @@ sub pluginmain { my $class = shift; my $hive = shift; #::logMsg("Launching shellfolders v.".$VERSION); - my $reg = Parse::Win32Registry->new($hive); - my $root_key = $reg->get_root_key; + if (defined(Parse::Win32Registry->new($hive))) { + my $reg = Parse::Win32Registry->new($hive); - my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; - my $key; - if ($key = $root_key->get_subkey($key_path)) { - ::rptMsg(""); - ::rptMsg("".gmtime($key->get_timestamp()).""); - - my @vals = $key->get_list_of_values(); - ::rptMsg(""); - if (scalar(@vals) > 0) { - foreach my $v (@vals) { - my $str = sprintf "%-20s %-40s","get_name()."\">",$v->get_data().""; - ::rptMsg($str); - } - ::rptMsg(""); - } - else { - #::rptMsg($key_path." has no values."); - } - ::rptMsg(""); - } - else { - #::rptMsg($key_path." not found."); - #::logMsg($key_path." not found."); - } + my $root_key = $reg->get_root_key; + + my $key_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + my $key; + if ($key = $root_key->get_subkey($key_path)) { + ::rptMsg(""); + ::rptMsg("".gmtime($key->get_timestamp()).""); + + my @vals = $key->get_list_of_values(); + ::rptMsg(""); + if (scalar(@vals) > 0) { + foreach my $v (@vals) { + my $str = sprintf "%-20s %-40s","get_name()."\">",$v->get_data().""; + ::rptMsg($str); + } + ::rptMsg(""); + } + else { + #::rptMsg($key_path." has no values."); + } + ::rptMsg(""); + } + else { + #::rptMsg($key_path." not found."); + #::logMsg($key_path." not found."); + } + } } 1; diff --git a/thirdparty/rr/plugins/officedocs.pl b/thirdparty/rr/plugins/officedocs.pl index 707a5c254f..fac96f52ff 100644 --- a/thirdparty/rr/plugins/officedocs.pl +++ b/thirdparty/rr/plugins/officedocs.pl @@ -37,115 +37,117 @@ sub pluginmain { # ::rptMsg("officedocs v.".$VERSION); # 20110830 [fpi] + banner # ::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner ::rptMsg(""); - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - #::rptMsg("officedocs v.".$VERSION); -# First, let's find out which version of Office is installed - my $version; - my $tag = 0; - my @versions = ("7\.0","8\.0", "9\.0", "10\.0", "11\.0","12\.0"); - foreach my $ver (@versions) { - my $key_path = "Software\\Microsoft\\Office\\".$ver."\\Common\\Open Find"; - if (defined($root_key->get_subkey($key_path))) { - $version = $ver; - $tag = 1; - } - } - - if ($tag) { - #::rptMsg("MSOffice version ".$version." located."); - my $key_path = "Software\\Microsoft\\Office\\".$version; - my $of_key = $root_key->get_subkey($key_path); - ::rptMsg(" ".gmtime($of_key->get_timestamp()).""); - ::rptMsg(""); - if ($of_key) { -# Attempt to retrieve Word docs - my @funcs = ("Open","Save As","File Save"); - foreach my $func (@funcs) { - my $word = "Common\\Open Find\\Microsoft Office Word\\Settings\\".$func."\\File Name MRU"; - my $word_key = $of_key->get_subkey($word); - if ($word_key) { - #::rptMsg($word); - - #::rptMsg(""); - my $value = $word_key->get_value("Value")->get_data(); - my @data = split(/\00/,$value); - ::rptMsg("". @data . ""); - #map{::rptMsg("$_");}@data; - } - else { -# ::rptMsg("Could not access ".$word); - } - #::rptMsg(""); - } -# Attempt to retrieve Excel docs - my $excel = 'Excel\\Recent Files'; - if (my $excel_key = $of_key->get_subkey($excel)) { - #::rptMsg($key_path."\\".$excel); - #::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); - my @vals = $excel_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - my $tag = (split(/File/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - #::rptMsg($key_path.$excel." has no values."); - } - } - else { - #::rptMsg($key_path.$excel." not found."); - } - #::rptMsg(""); -# Attempt to retrieve PowerPoint docs - my $ppt = 'PowerPoint\\Recent File List'; - if (my $ppt_key = $of_key->get_subkey($ppt)) { - #::rptMsg($key_path."\\".$ppt); - #::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); - my @vals = $ppt_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - my $data = $v->get_data(); - my $tag = (split(/File/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - #::rptMsg($key_path."\\".$ppt." has no values."); - } - } - else { - #::rptMsg($key_path."\\".$ppt." not found."); - } - } - else { - #::rptMsg("Could not access ".$key_path); - #::logMsg("Could not access ".$key_path); - } - ::rptMsg(""); - } - else { - #::logMsg("MSOffice version not found."); - #::rptMsg("MSOffice version not found."); - } + if (defined(Parse::Win32Registry->new($ntuser))) { + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + #::rptMsg("officedocs v.".$VERSION); + # First, let's find out which version of Office is installed + my $version; + my $tag = 0; + my @versions = ("7\.0","8\.0", "9\.0", "10\.0", "11\.0","12\.0"); + foreach my $ver (@versions) { + my $key_path = "Software\\Microsoft\\Office\\".$ver."\\Common\\Open Find"; + if (defined($root_key->get_subkey($key_path))) { + $version = $ver; + $tag = 1; + } + } + + if ($tag) { + #::rptMsg("MSOffice version ".$version." located."); + my $key_path = "Software\\Microsoft\\Office\\".$version; + my $of_key = $root_key->get_subkey($key_path); + ::rptMsg(" ".gmtime($of_key->get_timestamp()).""); + ::rptMsg(""); + if ($of_key) { + # Attempt to retrieve Word docs + my @funcs = ("Open","Save As","File Save"); + foreach my $func (@funcs) { + my $word = "Common\\Open Find\\Microsoft Office Word\\Settings\\".$func."\\File Name MRU"; + my $word_key = $of_key->get_subkey($word); + if ($word_key) { + #::rptMsg($word); + + #::rptMsg(""); + my $value = $word_key->get_value("Value")->get_data(); + my @data = split(/\00/,$value); + ::rptMsg("". @data . ""); + #map{::rptMsg("$_");}@data; + } + else { + # ::rptMsg("Could not access ".$word); + } + #::rptMsg(""); + } + # Attempt to retrieve Excel docs + my $excel = 'Excel\\Recent Files'; + if (my $excel_key = $of_key->get_subkey($excel)) { + #::rptMsg($key_path."\\".$excel); + #::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); + my @vals = $excel_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + my $data = $v->get_data(); + my $tag = (split(/File/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + #::rptMsg($key_path.$excel." has no values."); + } + } + else { + #::rptMsg($key_path.$excel." not found."); + } + #::rptMsg(""); + # Attempt to retrieve PowerPoint docs + my $ppt = 'PowerPoint\\Recent File List'; + if (my $ppt_key = $of_key->get_subkey($ppt)) { + #::rptMsg($key_path."\\".$ppt); + #::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); + my @vals = $ppt_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + my $data = $v->get_data(); + my $tag = (split(/File/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + #::rptMsg($key_path."\\".$ppt." has no values."); + } + } + else { + #::rptMsg($key_path."\\".$ppt." not found."); + } + } + else { + #::rptMsg("Could not access ".$key_path); + #::logMsg("Could not access ".$key_path); + } + ::rptMsg(""); + } + else { + #::logMsg("MSOffice version not found."); + #::rptMsg("MSOffice version not found."); + } + } ::rptMsg(""); } diff --git a/thirdparty/rr/plugins/officedocs2010.pl b/thirdparty/rr/plugins/officedocs2010.pl index 2783dc01f6..15073deed3 100644 --- a/thirdparty/rr/plugins/officedocs2010.pl +++ b/thirdparty/rr/plugins/officedocs2010.pl @@ -72,150 +72,151 @@ sub pluginmain { #::logMsg("Launching officedocs2010 v.".$VERSION); #::rptMsg("officedocs2010 v.".$VERSION); # 20110830 [fpi] + banner #::rptMsg("(".getHive().") ".getShortDescr()."\n"); # 20110830 [fpi] + banner - - my $reg = Parse::Win32Registry->new($ntuser); - my $root_key = $reg->get_root_key; - # ::rptMsg("officedocs v.".$VERSION); # 20110830 [fpi] - redundant - my $tag = 0; - my $key_path = "Software\\Microsoft\\Office\\14.0"; - if (defined($root_key->get_subkey($key_path))) { - $tag = 1; - } - - if ($tag) { - #::rptMsg("MSOffice version 2010 located."); - my $key_path = "Software\\Microsoft\\Office\\14.0"; - my $of_key = $root_key->get_subkey($key_path); - if ($of_key) { -# Attempt to retrieve Word docs - my $word = 'Word\\File MRU'; - if (my $word_key = $of_key->get_subkey($word)) { - #::rptMsg($key_path."\\".$word); - #::rptMsg("LastWrite Time ".gmtime($word_key->get_timestamp())." (UTC)"); - my @vals = $word_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - #::rptMsg($key_path.$word." has no values."); - } - } - else { - #::rptMsg($key_path.$word." not found."); - } - #::rptMsg(""); -# Attempt to retrieve Excel docs - my $excel = 'Excel\\File MRU'; - if (my $excel_key = $of_key->get_subkey($excel)) { - #::rptMsg($key_path."\\".$excel); - #::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); - my @vals = $excel_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - #::rptMsg($key_path.$excel." has no values."); - } - } - else { - #::rptMsg($key_path.$excel." not found."); - } - #::rptMsg(""); -# Attempt to retrieve Access docs - my $access = 'Access\\File MRU'; - if (my $access_key = $of_key->get_subkey($access)) { - #::rptMsg($key_path."\\".$access); - #::rptMsg("LastWrite Time ".gmtime($access_key->get_timestamp())." (UTC)"); - my @vals = $access_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - # ::rptMsg($key_path.$access." has no values."); - } - } - else { - # ::rptMsg($key_path.$access." not found."); - } - #::rptMsg(""); -# Attempt to retrieve PowerPoint docs - my $ppt = 'PowerPoint\\File MRU'; - if (my $ppt_key = $of_key->get_subkey($ppt)) { - #::rptMsg($key_path."\\".$ppt); - #::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); - my @vals = $ppt_key->get_list_of_values(); - if (scalar(@vals) > 0) { - my %files; -# Retrieve values and load into a hash for sorting - foreach my $v (@vals) { - my $val = $v->get_name(); - if ($val eq "Max Display") { next; } - my $data = getWinTS($v->get_data()); - my $tag = (split(/Item/,$val))[1]; - $files{$tag} = $val.":".$data; - } -# Print sorted content to report file - foreach my $u (sort {$a <=> $b} keys %files) { - my ($val,$data) = split(/:/,$files{$u},2); - ::rptMsg("".$data . ""); - } - } - else { - # ::rptMsg($key_path."\\".$ppt." has no values."); - } - } - else { - # ::rptMsg($key_path."\\".$ppt." not found."); - } - } - else { - # ::rptMsg("Could not access ".$key_path); - # ::logMsg("Could not access ".$key_path); - } - } - else { - # ::logMsg("MSOffice version not found."); - # ::rptMsg("MSOffice version not found."); - } + if (defined(Parse::Win32Registry->new($ntuser))) { + my $reg = Parse::Win32Registry->new($ntuser); + my $root_key = $reg->get_root_key; + # ::rptMsg("officedocs v.".$VERSION); # 20110830 [fpi] - redundant + my $tag = 0; + my $key_path = "Software\\Microsoft\\Office\\14.0"; + if (defined($root_key->get_subkey($key_path))) { + $tag = 1; + } + + if ($tag) { + #::rptMsg("MSOffice version 2010 located."); + my $key_path = "Software\\Microsoft\\Office\\14.0"; + my $of_key = $root_key->get_subkey($key_path); + if ($of_key) { + # Attempt to retrieve Word docs + my $word = 'Word\\File MRU'; + if (my $word_key = $of_key->get_subkey($word)) { + #::rptMsg($key_path."\\".$word); + #::rptMsg("LastWrite Time ".gmtime($word_key->get_timestamp())." (UTC)"); + my @vals = $word_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + if ($val eq "Max Display") { next; } + my $data = getWinTS($v->get_data()); + my $tag = (split(/Item/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + #::rptMsg($key_path.$word." has no values."); + } + } + else { + #::rptMsg($key_path.$word." not found."); + } + #::rptMsg(""); + # Attempt to retrieve Excel docs + my $excel = 'Excel\\File MRU'; + if (my $excel_key = $of_key->get_subkey($excel)) { + #::rptMsg($key_path."\\".$excel); + #::rptMsg("LastWrite Time ".gmtime($excel_key->get_timestamp())." (UTC)"); + my @vals = $excel_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + if ($val eq "Max Display") { next; } + my $data = getWinTS($v->get_data()); + my $tag = (split(/Item/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + #::rptMsg($key_path.$excel." has no values."); + } + } + else { + #::rptMsg($key_path.$excel." not found."); + } + #::rptMsg(""); + # Attempt to retrieve Access docs + my $access = 'Access\\File MRU'; + if (my $access_key = $of_key->get_subkey($access)) { + #::rptMsg($key_path."\\".$access); + #::rptMsg("LastWrite Time ".gmtime($access_key->get_timestamp())." (UTC)"); + my @vals = $access_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + if ($val eq "Max Display") { next; } + my $data = getWinTS($v->get_data()); + my $tag = (split(/Item/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + # ::rptMsg($key_path.$access." has no values."); + } + } + else { + # ::rptMsg($key_path.$access." not found."); + } + #::rptMsg(""); + # Attempt to retrieve PowerPoint docs + my $ppt = 'PowerPoint\\File MRU'; + if (my $ppt_key = $of_key->get_subkey($ppt)) { + #::rptMsg($key_path."\\".$ppt); + #::rptMsg("LastWrite Time ".gmtime($ppt_key->get_timestamp())." (UTC)"); + my @vals = $ppt_key->get_list_of_values(); + if (scalar(@vals) > 0) { + my %files; + # Retrieve values and load into a hash for sorting + foreach my $v (@vals) { + my $val = $v->get_name(); + if ($val eq "Max Display") { next; } + my $data = getWinTS($v->get_data()); + my $tag = (split(/Item/,$val))[1]; + $files{$tag} = $val.":".$data; + } + # Print sorted content to report file + foreach my $u (sort {$a <=> $b} keys %files) { + my ($val,$data) = split(/:/,$files{$u},2); + ::rptMsg("".$data . ""); + } + } + else { + # ::rptMsg($key_path."\\".$ppt." has no values."); + } + } + else { + # ::rptMsg($key_path."\\".$ppt." not found."); + } + } + else { + # ::rptMsg("Could not access ".$key_path); + # ::logMsg("Could not access ".$key_path); + } + } + else { + # ::logMsg("MSOffice version not found."); + # ::rptMsg("MSOffice version not found."); + } + } } 1;