Autopsy 4.23.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Case.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2012-2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.casemodule;
20
21import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
22import com.google.common.annotations.Beta;
23import com.google.common.eventbus.Subscribe;
24import com.google.common.util.concurrent.ThreadFactoryBuilder;
25import java.awt.Cursor;
26import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
27import java.awt.Frame;
28import java.awt.GraphicsEnvironment;
29import java.awt.event.ActionEvent;
30import java.awt.event.ActionListener;
31import java.beans.PropertyChangeListener;
32import java.beans.PropertyChangeSupport;
33import java.io.File;
34import java.lang.reflect.InvocationTargetException;
35import java.nio.file.InvalidPathException;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.sql.Connection;
39import java.sql.DriverManager;
40import java.sql.ResultSet;
41import java.sql.SQLException;
42import java.sql.Statement;
43import java.text.SimpleDateFormat;
44import java.util.ArrayList;
45import java.util.Collection;
46import java.util.Date;
47import java.util.HashMap;
48import java.util.HashSet;
49import java.util.List;
50import java.util.Map;
51import java.util.Optional;
52import java.util.Set;
53import java.util.TimeZone;
54import java.util.UUID;
55import java.util.concurrent.CancellationException;
56import java.util.concurrent.ExecutionException;
57import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
59import java.util.concurrent.Future;
60import java.util.concurrent.ThreadFactory;
61import java.util.concurrent.TimeUnit;
62import java.util.logging.Level;
63import java.util.stream.Collectors;
64import java.util.stream.Stream;
65import javax.annotation.concurrent.GuardedBy;
66import javax.annotation.concurrent.ThreadSafe;
67import javax.swing.JOptionPane;
68import javax.swing.SwingUtilities;
69import org.apache.commons.lang3.ArrayUtils;
70import org.apache.commons.lang3.StringUtils;
71import org.openide.util.Lookup;
72import org.openide.util.NbBundle;
73import org.openide.util.NbBundle.Messages;
74import org.openide.util.actions.CallableSystemAction;
75import org.openide.windows.WindowManager;
76import org.sleuthkit.autopsy.actions.OpenOutputFolderAction;
77import org.sleuthkit.autopsy.appservices.AutopsyService;
78import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext;
79import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException;
80import org.sleuthkit.autopsy.datasourcesummary.ui.DataSourceSummaryAction;
81import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent;
82import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent;
83import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
84import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
85import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
86import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
87import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
88import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
89import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
90import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
91import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent;
92import org.sleuthkit.autopsy.casemodule.events.HostsAddedToPersonEvent;
93import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent;
94import org.sleuthkit.autopsy.casemodule.events.HostsDeletedEvent;
95import org.sleuthkit.autopsy.casemodule.events.HostsRemovedFromPersonEvent;
96import org.sleuthkit.autopsy.casemodule.events.OsAccountsAddedEvent;
97import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent;
98import org.sleuthkit.autopsy.casemodule.events.OsAccountsDeletedEvent;
99import org.sleuthkit.autopsy.casemodule.events.OsAcctInstancesAddedEvent;
100import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
101import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent;
102import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
103import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
104import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesAddedEvent;
105import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesDeletedEvent;
106import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesUpdatedEvent;
107import org.sleuthkit.autopsy.casemodule.events.TagSetsEvent.TagSetsAddedEvent;
108import org.sleuthkit.autopsy.casemodule.events.TagSetsEvent.TagSetsDeletedEvent;
109import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
110import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
111import org.sleuthkit.autopsy.casemodule.services.Services;
112import org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributeSearchAction;
113import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction;
114import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
115import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
116import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
117import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
118import org.sleuthkit.autopsy.core.RuntimeProperties;
119import org.sleuthkit.autopsy.core.UserPreferences;
120import org.sleuthkit.autopsy.core.UserPreferencesException;
121import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl;
122import org.sleuthkit.autopsy.coreutils.DriveUtils;
123import org.sleuthkit.autopsy.coreutils.FileUtil;
124import org.sleuthkit.autopsy.coreutils.Logger;
125import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
126import org.sleuthkit.autopsy.coreutils.NetworkUtils;
127import org.sleuthkit.autopsy.coreutils.PlatformUtil;
128import org.sleuthkit.autopsy.coreutils.ThreadUtils;
129import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
130import org.sleuthkit.autopsy.coreutils.Version;
131import org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction;
132import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
133import org.sleuthkit.autopsy.events.AutopsyEvent;
134import org.sleuthkit.autopsy.events.AutopsyEventException;
135import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
136import org.sleuthkit.autopsy.discovery.ui.OpenDiscoveryAction;
137import org.sleuthkit.autopsy.ingest.IngestJob;
138import org.sleuthkit.autopsy.ingest.IngestManager;
139import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
140import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
141import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
142import org.sleuthkit.autopsy.progress.LoggingProgressIndicator;
143import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
144import org.sleuthkit.autopsy.progress.ProgressIndicator;
145import org.sleuthkit.autopsy.timeline.OpenTimelineAction;
146import org.sleuthkit.autopsy.timeline.events.TimelineEventAddedEvent;
147import org.sleuthkit.datamodel.BlackboardArtifactTag;
148import org.sleuthkit.datamodel.CaseDbConnectionInfo;
149import org.sleuthkit.datamodel.ConcurrentDbAccessException;
150import org.sleuthkit.datamodel.Content;
151import org.sleuthkit.datamodel.ContentStreamProvider;
152import org.sleuthkit.datamodel.ContentTag;
153import org.sleuthkit.datamodel.DataSource;
154import org.sleuthkit.datamodel.FileSystem;
155import org.sleuthkit.datamodel.Image;
156import org.sleuthkit.datamodel.Report;
157import org.sleuthkit.datamodel.SleuthkitCase;
158import org.sleuthkit.datamodel.TimelineManager;
159import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
160import org.sleuthkit.datamodel.TskCoreException;
161import org.sleuthkit.datamodel.TskDataException;
162import org.sleuthkit.datamodel.TskEvent;
163import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
164
168public class Case {
169
170 private static final int CASE_LOCK_TIMEOUT_MINS = 1;
171 private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1;
172 private static final String APP_NAME = UserPreferences.getAppName();
173 private static final String TEMP_FOLDER = "Temp";
174 private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
175 private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
176 private static final String CACHE_FOLDER = "Cache"; //NON-NLS
177 private static final String EXPORT_FOLDER = "Export"; //NON-NLS
178 private static final String LOG_FOLDER = "Log"; //NON-NLS
179 private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
180 private static final String CONFIG_FOLDER = "Config"; // NON-NLS
181 private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
182 private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
183 private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
184 private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode";
185 private static final Logger logger = Logger.getLogger(Case.class.getName());
187 private static final Object caseActionSerializationLock = new Object();
188 private static Future<?> backgroundOpenFileSystemsFuture = null;
189 private static final ExecutorService openFileSystemsExecutor
190 = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("case-open-file-systems-%d").build());
191 private static volatile Frame mainFrame;
192 private static volatile Case currentCase;
193 private final CaseMetadata metadata;
194 private volatile ExecutorService caseActionExecutor;
196 private SleuthkitCase caseDb;
198 private CollaborationMonitor collaborationMonitor;
200
201 private volatile boolean hasDataSource = false;
202 private volatile boolean hasData = false;
203
204 /*
205 * Get a reference to the main window of the desktop application to use to
206 * parent pop up dialogs and initialize the application name for use in
207 * changing the main window title.
208 */
209 static {
210 if (!GraphicsEnvironment.isHeadless()) {
211 WindowManager.getDefault().invokeWhenUIReady(() -> {
212 mainFrame = WindowManager.getDefault().getMainWindow();
213 });
214 }
215 }
216
220 public enum CaseType {
221
222 SINGLE_USER_CASE("Single-user case"), //NON-NLS
223 MULTI_USER_CASE("Multi-user case"); //NON-NLS
224
225 private final String typeName;
226
234 public static CaseType fromString(String typeName) {
235 if (typeName != null) {
236 for (CaseType c : CaseType.values()) {
237 if (typeName.equalsIgnoreCase(c.toString())) {
238 return c;
239 }
240 }
241 }
242 return null;
243 }
244
250 @Override
251 public String toString() {
252 return typeName;
253 }
254
260 @Messages({
261 "Case_caseType_singleUser=Single-user case",
262 "Case_caseType_multiUser=Multi-user case"
263 })
266 return Bundle.Case_caseType_singleUser();
267 } else {
268 return Bundle.Case_caseType_multiUser();
269 }
270 }
271
277 private CaseType(String typeName) {
278 this.typeName = typeName;
279 }
280
291 @Deprecated
292 public boolean equalsName(String otherTypeName) {
293 return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
294 }
295
296 };
297
520
526 private final class SleuthkitEventListener {
527
528 @Subscribe
529 public void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event) {
530 eventPublisher.publish(new TimelineEventAddedEvent(event));
531 }
532
533 @Subscribe
534 public void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
535 hasData = true;
536 eventPublisher.publish(new OsAccountsAddedEvent(event.getOsAcounts()));
537 }
538
539 @Subscribe
540 public void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event) {
541 eventPublisher.publish(new OsAccountsUpdatedEvent(event.getOsAcounts()));
542 }
543
544 @Subscribe
545 public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
546 try {
547 hasData = dbHasData();
548 } catch (TskCoreException ex) {
549 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
550 }
551 eventPublisher.publish(new OsAccountsDeletedEvent(event.getOsAccountObjectIds()));
552 }
553
554 @Subscribe
555 public void publishOsAccountInstancesAddedEvent(TskEvent.OsAcctInstancesAddedTskEvent event) {
556 eventPublisher.publish(new OsAcctInstancesAddedEvent(event.getOsAccountInstances()));
557 }
558
565 @Subscribe
566 public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
567 hasData = true;
568 eventPublisher.publish(new HostsAddedEvent(event.getHosts()));
569 }
570
577 @Subscribe
578 public void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event) {
579 eventPublisher.publish(new HostsUpdatedEvent(event.getHosts()));
580 }
581
588 @Subscribe
589 public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
590 try {
591 hasData = dbHasData();
592 } catch (TskCoreException ex) {
593 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
594 }
595
596 eventPublisher.publish(new HostsDeletedEvent(event.getHostIds()));
597 }
598
605 @Subscribe
606 public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
607 eventPublisher.publish(new PersonsAddedEvent(event.getPersons()));
608 }
609
616 @Subscribe
617 public void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event) {
618 eventPublisher.publish(new PersonsUpdatedEvent(event.getPersons()));
619 }
620
627 @Subscribe
628 public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
629 eventPublisher.publish(new PersonsDeletedEvent(event.getPersonIds()));
630 }
631
632 @Subscribe
633 public void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event) {
634 eventPublisher.publish(new HostsAddedToPersonEvent(event.getPerson(), event.getHosts()));
635 }
636
637 @Subscribe
638 public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) {
639 eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHostIds()));
640 }
641
642 @Subscribe
643 public void publicTagNamesAdded(TskEvent.TagNamesAddedTskEvent event) {
644 eventPublisher.publish(new TagNamesAddedEvent(event.getTagNames()));
645 }
646
647 @Subscribe
648 public void publicTagNamesUpdated(TskEvent.TagNamesUpdatedTskEvent event) {
649 eventPublisher.publish(new TagNamesUpdatedEvent(event.getTagNames()));
650 }
651
652 @Subscribe
653 public void publicTagNamesDeleted(TskEvent.TagNamesDeletedTskEvent event) {
654 eventPublisher.publish(new TagNamesDeletedEvent(event.getTagNameIds()));
655 }
656
657 @Subscribe
658 public void publicTagSetsAdded(TskEvent.TagSetsAddedTskEvent event) {
659 eventPublisher.publish(new TagSetsAddedEvent(event.getTagSets()));
660 }
661
662 @Subscribe
663 public void publicTagSetsDeleted(TskEvent.TagSetsDeletedTskEvent event) {
664 eventPublisher.publish(new TagSetsDeletedEvent(event.getTagSetIds()));
665 }
666 }
667
674 public static void addPropertyChangeListener(PropertyChangeListener listener) {
675 addEventSubscriber(Stream.of(Events.values())
676 .map(Events::toString)
677 .collect(Collectors.toSet()), listener);
678 }
679
686 public static void removePropertyChangeListener(PropertyChangeListener listener) {
687 removeEventSubscriber(Stream.of(Events.values())
688 .map(Events::toString)
689 .collect(Collectors.toSet()), listener);
690 }
691
700 @Deprecated
701 public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
702 eventPublisher.addSubscriber(eventNames, subscriber);
703 }
704
711 public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
712 eventTypes.forEach((Events event) -> {
713 eventPublisher.addSubscriber(event.toString(), subscriber);
714 });
715 }
716
725 @Deprecated
726 public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
727 eventPublisher.addSubscriber(eventName, subscriber);
728 }
729
736 public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
737 eventPublisher.removeSubscriber(eventName, subscriber);
738 }
739
746 public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
747 eventPublisher.removeSubscriber(eventNames, subscriber);
748 }
749
756 public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
757 eventTypes.forEach((Events event) -> {
758 eventPublisher.removeSubscriber(event.toString(), subscriber);
759 });
760 }
761
770 public static boolean isValidName(String caseName) {
771 return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
772 || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
773 || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
774 }
775
800 @Deprecated
801 public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
802 createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
803 }
804
824 @Messages({
825 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
826 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
827 })
828 public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
829 if (caseDetails.getCaseDisplayName().isEmpty()) {
830 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
831 }
832 if (caseDir.isEmpty()) {
833 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
834 }
835 openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
836 }
837
851 @Messages({
852 "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
853 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
854 })
855 public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
857 try {
858 metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
859 } catch (CaseMetadataException ex) {
860 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
861 }
863 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
864 }
865 openAsCurrentCase(new Case(metadata), false);
866 }
867
873 public static boolean isCaseOpen() {
874 return currentCase != null;
875 }
876
884 public static Case getCurrentCase() {
885 try {
886 return getCurrentCaseThrows();
887 } catch (NoCurrentCaseException ex) {
888 /*
889 * Throw a runtime exception, since this is a programming error.
890 */
891 throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
892 }
893 }
894
910 /*
911 * TODO (JIRA-3825): Introduce a reference counting scheme for this get
912 * case method.
913 */
914 Case openCase = currentCase;
915 if (openCase == null) {
916 throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
917 } else {
918 return openCase;
919 }
920 }
921
930 @Messages({
931 "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
932 "Case.progressIndicatorTitle.closingCase=Closing Case"
933 })
934 public static void closeCurrentCase() throws CaseActionException {
935 synchronized (caseActionSerializationLock) {
936 if (null == currentCase) {
937 return;
938 }
939 Case closedCase = currentCase;
940 try {
941 eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
942 logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
943 closedCase.doCloseCaseAction();
944 logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
945 } catch (CaseActionException ex) {
946 logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
947 throw ex;
948 } finally {
949 currentCase = null;
952 }
953 }
954 }
955 }
956
965 public static void deleteCurrentCase() throws CaseActionException {
966 synchronized (caseActionSerializationLock) {
967 if (null == currentCase) {
968 return;
969 }
970 CaseMetadata metadata = currentCase.getMetadata();
973 }
974 }
975
986 @Messages({
987 "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
988 })
989 static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
990 synchronized (caseActionSerializationLock) {
991 if (null == currentCase) {
992 return;
993 }
994
995 /*
996 * Close the current case to release the shared case lock.
997 */
998 CaseMetadata caseMetadata = currentCase.getMetadata();
1000
1001 /*
1002 * Re-open the case with an exclusive case lock, delete the data
1003 * source, and close the case again, releasing the exclusive case
1004 * lock.
1005 */
1006 Case theCase = new Case(caseMetadata);
1007 theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
1008
1009 /*
1010 * Re-open the case with a shared case lock.
1011 */
1012 openAsCurrentCase(new Case(caseMetadata), false);
1013 }
1014 }
1015
1027 @Messages({
1028 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
1029 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
1030 "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
1031 })
1033 synchronized (caseActionSerializationLock) {
1034 if (null != currentCase) {
1035 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
1036 }
1037 }
1038
1039 ProgressIndicator progressIndicator;
1041 progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
1042 } else {
1043 progressIndicator = new LoggingProgressIndicator();
1044 }
1045 progressIndicator.start(Bundle.Case_progressMessage_preparing());
1046 try {
1047 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1048 deleteSingleUserCase(metadata, progressIndicator);
1049 } else {
1050 try {
1051 deleteMultiUserCase(metadata, progressIndicator);
1052 } catch (InterruptedException ex) {
1053 /*
1054 * Note that task cancellation is not currently supported
1055 * for this code path, so this catch block is not expected
1056 * to be executed.
1057 */
1058 throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.getCaseDisplayName()), ex);
1059 }
1060 }
1061 } finally {
1062 progressIndicator.finish();
1063 }
1064 }
1065
1076 @Messages({
1077 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1078 "Case.progressIndicatorTitle.openingCase=Opening Case",
1079 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
1080 })
1081 private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
1082 synchronized (caseActionSerializationLock) {
1083 if (null != currentCase) {
1084 try {
1086 } catch (CaseActionException ex) {
1087 /*
1088 * Notify the user and continue (the error has already been
1089 * logged in closeCurrentCase.
1090 */
1091 MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
1092 }
1093 }
1094 try {
1095 logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
1096 String progressIndicatorTitle;
1098 if (isNewCase) {
1099 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
1100 openCaseAction = newCurrentCase::create;
1101 } else {
1102 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
1103 openCaseAction = newCurrentCase::open;
1104 }
1105 newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null);
1106 currentCase = newCurrentCase;
1107 logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
1109 updateGUIForCaseOpened(newCurrentCase);
1110 }
1111 eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
1112 } catch (CaseActionCancelledException ex) {
1113 logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
1114 throw ex;
1115 } catch (CaseActionException ex) {
1116 logger.log(Level.SEVERE, String.format("Error opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()), ex); //NON-NLS
1117 throw ex;
1118 }
1119 }
1120 }
1121
1130 private static String displayNameToUniqueName(String caseDisplayName) {
1131 /*
1132 * Replace all non-ASCII characters.
1133 */
1134 String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
1135
1136 /*
1137 * Replace all control characters.
1138 */
1139 uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
1140
1141 /*
1142 * Replace /, \, :, ?, space, ' ".
1143 */
1144 uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
1145
1146 /*
1147 * Make it all lowercase.
1148 */
1149 uniqueCaseName = uniqueCaseName.toLowerCase();
1150
1151 /*
1152 * Add a time stamp for uniqueness.
1153 */
1154 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
1155 Date date = new Date();
1156 uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
1157
1158 return uniqueCaseName;
1159 }
1160
1170 public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
1171 /*
1172 * Check the case directory path and permissions. The case directory may
1173 * already exist.
1174 */
1175 File caseDir = new File(caseDirPath);
1176 if (caseDir.exists()) {
1177 if (caseDir.isFile()) {
1178 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
1179 } else if (!caseDir.canRead() || !caseDir.canWrite()) {
1180 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
1181 }
1182 }
1183
1184 /*
1185 * Create the case directory, if it does not already exist.
1186 */
1187 if (!caseDir.mkdirs()) {
1188 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
1189 }
1190
1191 /*
1192 * Create the subdirectories of the case directory, if they do not
1193 * already exist. Note that multi-user cases get an extra layer of
1194 * subdirectories, one subdirectory per application host machine.
1195 */
1196 String hostPathComponent = "";
1197 if (caseType == CaseType.MULTI_USER_CASE) {
1198 hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
1199 }
1200
1201 Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
1202 if (!exportDir.toFile().mkdirs()) {
1203 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
1204 }
1205
1206 Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
1207 if (!logsDir.toFile().mkdirs()) {
1208 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
1209 }
1210
1211 Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
1212 if (!cacheDir.toFile().mkdirs()) {
1213 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
1214 }
1215
1216 Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
1217 if (!moduleOutputDir.toFile().mkdirs()) {
1218 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
1219 }
1220
1221 Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
1222 if (!reportsDir.toFile().mkdirs()) {
1223 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
1224 }
1225 }
1226
1234 static Map<Long, String> getImagePaths(SleuthkitCase db) {
1235 Map<Long, String> imgPaths = new HashMap<>();
1236 try {
1237 Map<Long, List<String>> imgPathsList = db.getImagePaths();
1238 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
1239 if (entry.getValue().size() > 0) {
1240 imgPaths.put(entry.getKey(), entry.getValue().get(0));
1241 }
1242 }
1243 } catch (TskCoreException ex) {
1244 logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
1245 }
1246 return imgPaths;
1247 }
1248
1259 @Messages({
1260 "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
1261 })
1263 try {
1264 Path caseDirPath = Paths.get(caseDir);
1265 String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath);
1267 return lock;
1268 } catch (InterruptedException ex) {
1269 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
1270 } catch (CoordinationServiceException ex) {
1271 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1272 }
1273 }
1274
1275 private static String getNameForTitle() {
1276 //Method should become unnecessary once technical debt story 3334 is done.
1277 if (UserPreferences.getAppName().equals(Version.getName())) {
1278 //Available version number is version number for this application
1279 return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
1280 } else {
1281 return UserPreferences.getAppName();
1282 }
1283 }
1284
1288 private static void updateGUIForCaseOpened(Case newCurrentCase) {
1289 /*
1290 * If the case database was upgraded for a new schema and a backup
1291 * database was created, notify the user.
1292 */
1293 SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
1294 String backupDbPath = caseDb.getBackupDatabasePath();
1295 if (null != backupDbPath) {
1296 JOptionPane.showMessageDialog(
1297 mainFrame,
1298 NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
1299 NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1300 JOptionPane.INFORMATION_MESSAGE);
1301 }
1302
1303 /*
1304 * Look for the files for the data sources listed in the case database
1305 * and give the user the opportunity to locate any that are missing.
1306 */
1307 Map<Long, String> imgPaths = getImagePaths(caseDb);
1308 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1309 long obj_id = entry.getKey();
1310 String path = entry.getValue();
1311 boolean fileExists = (new File(path).exists()|| DriveUtils.driveExists(path));
1312 if (!fileExists) {
1313 try {
1314 DataSource ds = newCurrentCase.getSleuthkitCase().getDataSource(obj_id);
1315 String hostName = StringUtils.defaultString(ds.getHost() == null ? "" : ds.getHost().getName());
1316 // Using invokeAndWait means that the dialog will
1317 // open on the EDT but this thread will wait for an
1318 // answer. Using invokeLater would cause this loop to
1319 // end before all of the dialogs appeared.
1320 SwingUtilities.invokeAndWait(new Runnable() {
1321 @Override
1322 public void run() {
1323 int response = JOptionPane.showConfirmDialog(
1324 mainFrame,
1325 NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", hostName, path),
1326 NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1327 JOptionPane.YES_NO_OPTION);
1328 if (response == JOptionPane.YES_OPTION) {
1329 MissingImageDialog.makeDialog(obj_id, caseDb);
1330 } else {
1331 logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1332
1333 }
1334 }
1335
1336 });
1337 } catch (InterruptedException | InvocationTargetException | TskCoreException | TskDataException ex) {
1338 logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS
1339 }
1340 }
1341 }
1342
1343 /*
1344 * Enable the case-specific actions.
1345 */
1346 CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
1347 CallableSystemAction.get(OpenHostsAction.class).setEnabled(true);
1348 CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1349 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1350 CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
1351 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase());
1352 CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1353 CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1354 CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1355 CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1356 CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true);
1357
1358 /*
1359 * Add the case to the recent cases tracker that supplies a list of
1360 * recent cases to the recent cases menu item and the open/create case
1361 * dialog.
1362 */
1363 RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1364 final boolean hasData = newCurrentCase.hasData();
1365
1366 SwingUtilities.invokeLater(() -> {
1367 /*
1368 * Open the top components (windows within the main application
1369 * window).
1370 *
1371 * Note: If the core windows are not opened here, they will be
1372 * opened via the DirectoryTreeTopComponent 'propertyChange()'
1373 * method on a DATA_SOURCE_ADDED event.
1374 */
1375 mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1376 if (hasData) {
1378 } else {
1379 //ensure that the DirectoryTreeTopComponent is open so that it's listener can open the core windows including making it visible.
1381 }
1382 mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1383
1384 /*
1385 * Reset the main window title to:
1386 *
1387 * [curent case display name] - [application name].
1388 */
1389 mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1390 });
1391 }
1392
1393 /*
1394 * Update the GUI to to reflect the lack of a current case.
1395 */
1396 private static void updateGUIForCaseClosed() {
1398 SwingUtilities.invokeLater(() -> {
1399 /*
1400 * Close the top components (windows within the main application
1401 * window).
1402 */
1404
1405 /*
1406 * Disable the case-specific menu items.
1407 */
1408 CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1409 CallableSystemAction.get(OpenHostsAction.class).setEnabled(false);
1410 CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1411 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1412 CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
1413 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1414 CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1415 CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1416 CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1417 CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1418 CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(false);
1419
1420 /*
1421 * Clear the notifications in the notfier component in the lower
1422 * right hand corner of the main application window.
1423 */
1425
1426 /*
1427 * Reset the main window title to be just the application name,
1428 * instead of [curent case display name] - [application name].
1429 */
1430 mainFrame.setTitle(getNameForTitle());
1431 });
1432 }
1433 }
1434
1440 public SleuthkitCase getSleuthkitCase() {
1441 return this.caseDb;
1442 }
1443
1450 return caseServices;
1451 }
1452
1459 return metadata.getCaseType();
1460 }
1461
1467 public String getCreatedDate() {
1468 return metadata.getCreatedDate();
1469 }
1470
1476 public String getName() {
1477 return metadata.getCaseName();
1478 }
1479
1485 public String getDisplayName() {
1486 return metadata.getCaseDisplayName();
1487 }
1488
1494 public String getNumber() {
1495 return metadata.getCaseNumber();
1496 }
1497
1503 public String getExaminer() {
1504 return metadata.getExaminer();
1505 }
1506
1512 public String getExaminerPhone() {
1513 return metadata.getExaminerPhone();
1514 }
1515
1521 public String getExaminerEmail() {
1522 return metadata.getExaminerEmail();
1523 }
1524
1530 public String getCaseNotes() {
1531 return metadata.getCaseNotes();
1532 }
1533
1539 public String getCaseDirectory() {
1540 return metadata.getCaseDirectory();
1541 }
1542
1551 public String getOutputDirectory() {
1552 String caseDirectory = getCaseDirectory();
1553 Path hostPath;
1554 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1555 hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1556 } else {
1557 hostPath = Paths.get(caseDirectory);
1558 }
1559 if (!hostPath.toFile().exists()) {
1560 hostPath.toFile().mkdirs();
1561 }
1562 return hostPath.toString();
1563 }
1564
1568 private Path getBaseSystemTempPath() {
1569 return Paths.get(System.getProperty("java.io.tmpdir"), APP_NAME, getName());
1570 }
1571
1578 public String getTempDirectory() {
1579 // NOTE: UserPreferences may also be affected by changes in this method.
1580 // See JIRA-7505 for more information.
1581 Path basePath = null;
1582 // get base temp path for the case based on user preference
1584 case CUSTOM:
1585 String customDirectory = UserMachinePreferences.getCustomTempDirectory();
1586 basePath = (StringUtils.isBlank(customDirectory))
1587 ? null
1588 : Paths.get(customDirectory, APP_NAME, getName());
1589 break;
1590 case CASE:
1591 basePath = Paths.get(getCaseDirectory());
1592 break;
1593 case SYSTEM:
1594 default:
1595 // at this level, if the case directory is specified for a temp
1596 // directory, return the system temp directory instead.
1597 basePath = getBaseSystemTempPath();
1598 break;
1599 }
1600
1601 basePath = basePath == null ? getBaseSystemTempPath() : basePath;
1602
1603 // get sub directories based on multi user vs. single user
1604 Path caseRelPath = (CaseType.MULTI_USER_CASE.equals(getCaseType()))
1606 : Paths.get(TEMP_FOLDER);
1607
1608 File caseTempDir = basePath
1609 .resolve(caseRelPath)
1610 .toFile();
1611
1612 // ensure directory exists
1613 if (!caseTempDir.exists()) {
1614 caseTempDir.mkdirs();
1615 }
1616
1617 return caseTempDir.getAbsolutePath();
1618 }
1619
1626 public String getCacheDirectory() {
1628 }
1629
1636 public String getExportDirectory() {
1638 }
1639
1646 public String getLogDirectoryPath() {
1648 }
1649
1656 public String getReportDirectory() {
1658 }
1659
1666 public String getConfigDirectory() {
1668 }
1669
1676 public String getModuleDirectory() {
1678 }
1679
1688 Path path = Paths.get(getModuleDirectory());
1690 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1691 } else {
1692 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1693 }
1694 }
1695
1705 public List<Content> getDataSources() throws TskCoreException {
1706 return caseDb.getRootObjects();
1707 }
1708
1714 public Set<TimeZone> getTimeZones() {
1715 Set<TimeZone> timezones = new HashSet<>();
1716 String query = "SELECT time_zone FROM data_source_info";
1717 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
1718 ResultSet timeZoneSet = dbQuery.getResultSet();
1719 while (timeZoneSet.next()) {
1720 String timeZone = timeZoneSet.getString("time_zone");
1721 if (timeZone != null && !timeZone.isEmpty()) {
1722 timezones.add(TimeZone.getTimeZone(timeZone));
1723 }
1724 }
1725 } catch (TskCoreException | SQLException ex) {
1726 logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1727 }
1728 return timezones;
1729 }
1730
1737 public String getTextIndexName() {
1738 return getMetadata().getTextIndexName();
1739 }
1740
1746 public boolean hasData() {
1747 return hasData;
1748 }
1749
1755 public boolean hasDataSource() {
1756 return hasDataSource;
1757 }
1758
1769 public void notifyAddingDataSource(UUID eventId) {
1770 hasDataSource = true;
1771 hasData = true;
1772 eventPublisher.publish(new AddingDataSourceEvent(eventId));
1773 }
1774
1785 public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1786 eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1787 }
1788
1800 public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1801 eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1802 }
1803
1813 public void notifyDataSourceNameChanged(Content dataSource, String newName) {
1814 eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
1815 }
1816
1824 public void notifyContentTagAdded(ContentTag newTag) {
1825 notifyContentTagAdded(newTag, null);
1826 }
1827
1837 public void notifyContentTagAdded(ContentTag newTag, List<ContentTag> deletedTagList) {
1838 eventPublisher.publish(new ContentTagAddedEvent(newTag, deletedTagList));
1839 }
1840
1848 public void notifyContentTagDeleted(ContentTag deletedTag) {
1849 eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1850 }
1851
1859 public void notifyTagDefinitionChanged(String changedTagName) {
1860 //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1861 eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1862 }
1863
1874 public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1875 try {
1876 eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1877 } catch (NoCurrentCaseException ex) {
1878 logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1879 }
1880 }
1881
1889 public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1891 }
1892
1902 public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List<BlackboardArtifactTag> removedTagList) {
1903 eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag, removedTagList));
1904 }
1905
1913 public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1914 eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1915 }
1916
1928 public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1929 addReport(localPath, srcModuleName, reportName, null);
1930 }
1931
1946 public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1947 String normalizedLocalPath;
1948 try {
1949 if (localPath.toLowerCase().contains("http:")) {
1950 normalizedLocalPath = localPath;
1951 } else {
1952 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1953 }
1954 } catch (InvalidPathException ex) {
1955 String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1956 throw new TskCoreException(errorMsg, ex);
1957 }
1958 hasData = true;
1959
1960 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1962 return report;
1963 }
1964
1973 public List<Report> getAllReports() throws TskCoreException {
1974 return this.caseDb.getAllReports();
1975 }
1976
1985 public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1986 for (Report report : reports) {
1987 this.caseDb.deleteReport(report);
1988 }
1989
1990 try {
1991 hasData = dbHasData();
1992 } catch (TskCoreException ex) {
1993 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
1994 }
1995
1996 for (Report report : reports) {
1997 eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1998 }
1999 }
2000
2007 return metadata;
2008 }
2009
2017 @Messages({
2018 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
2019 })
2020 void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
2021 CaseDetails oldCaseDetails = metadata.getCaseDetails();
2022 try {
2023 metadata.setCaseDetails(caseDetails);
2024 } catch (CaseMetadataException ex) {
2025 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
2026 }
2027 if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
2028 try {
2029 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2030 nodeData.setDisplayName(caseDetails.getCaseDisplayName());
2031 CaseNodeData.writeCaseNodeData(nodeData);
2032 } catch (CaseNodeDataException | InterruptedException ex) {
2033 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2034 }
2035 }
2036 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
2037 eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
2038 }
2039 if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
2040 eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
2041 }
2042 if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
2043 eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
2044 }
2045 eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
2046 if (RuntimeProperties.runningWithGUI()) {
2047 SwingUtilities.invokeLater(() -> {
2048 mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
2049 try {
2050 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
2051 } catch (Exception ex) {
2052 logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
2053 }
2054 });
2055 }
2056 }
2057
2070 private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
2071 this(new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails));
2072 }
2073
2079 private Case(CaseMetadata caseMetaData) {
2080 metadata = caseMetaData;
2082 }
2083
2113 @Messages({
2114 "Case.progressIndicatorCancelButton.label=Cancel",
2115 "Case.progressMessage.preparing=Preparing...",
2116 "Case.progressMessage.cancelling=Cancelling...",
2117 "Case.exceptionMessage.cancelled=Cancelled.",
2118 "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
2119 })
2120 private void doOpenCaseAction(String progressIndicatorTitle, CaseAction<ProgressIndicator, Object, Void> caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException {
2121 /*
2122 * Create and start either a GUI progress indicator (with or without a
2123 * cancel button) or a logging progress indicator.
2124 */
2125 CancelButtonListener cancelButtonListener = null;
2126 ProgressIndicator progressIndicator;
2128 if (allowCancellation) {
2129 cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
2130 progressIndicator = new ModalDialogProgressIndicator(
2131 mainFrame,
2132 progressIndicatorTitle,
2133 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2134 Bundle.Case_progressIndicatorCancelButton_label(),
2135 cancelButtonListener);
2136 } else {
2137 progressIndicator = new ModalDialogProgressIndicator(
2138 mainFrame,
2139 progressIndicatorTitle);
2140 }
2141 } else {
2142 progressIndicator = new LoggingProgressIndicator();
2143 }
2144 progressIndicator.start(Bundle.Case_progressMessage_preparing());
2145
2146 /*
2147 * Do the case action in the single thread in the case action executor.
2148 * If the case is a multi-user case, a case lock is acquired and held
2149 * until explictly released and an exclusive case resources lock is
2150 * aquired and held for the duration of the action.
2151 */
2152 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
2153 caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory);
2154 Future<Void> future = caseActionExecutor.submit(() -> {
2155 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2156 caseAction.execute(progressIndicator, additionalParams);
2157 } else {
2158 acquireCaseLock(caseLockType);
2159 try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
2160 if (null == resourcesLock) {
2161 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2162 }
2163 caseAction.execute(progressIndicator, additionalParams);
2164 } catch (CaseActionException ex) {
2166 throw ex;
2167 }
2168 }
2169 return null;
2170 });
2171 if (null != cancelButtonListener) {
2172 cancelButtonListener.setCaseActionFuture(future);
2173 }
2174
2175 /*
2176 * Wait for the case action task to finish.
2177 */
2178 try {
2179 future.get();
2180 } catch (InterruptedException discarded) {
2181 /*
2182 * The thread this method is running in has been interrupted.
2183 */
2184 if (null != cancelButtonListener) {
2185 cancelButtonListener.actionPerformed(null);
2186 } else {
2187 future.cancel(true);
2188 }
2190 } catch (CancellationException discarded) {
2191 /*
2192 * The case action has been cancelled.
2193 */
2195 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2196 } catch (ExecutionException ex) {
2197 /*
2198 * The case action has thrown an exception.
2199 */
2201 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
2202 } finally {
2203 progressIndicator.finish();
2204 }
2205 }
2206
2222 private Void create(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2223 assert (additionalParams == null);
2224 try {
2226 createCaseDirectoryIfDoesNotExist(progressIndicator);
2228 switchLoggingToCaseLogsDirectory(progressIndicator);
2230 saveCaseMetadataToFile(progressIndicator);
2232 createCaseNodeData(progressIndicator);
2235 createCaseDatabase(progressIndicator);
2237 openCaseLevelServices(progressIndicator);
2239 openAppServiceCaseResources(progressIndicator, true);
2241 openCommunicationChannels(progressIndicator);
2242 return null;
2243
2244 } catch (CaseActionException ex) {
2245 /*
2246 * Cancellation or failure. The sleep is a little hack to clear the
2247 * interrupted flag for this thread if this is a cancellation
2248 * scenario, so that the clean up can run to completion in the
2249 * current thread.
2250 */
2251 try {
2252 Thread.sleep(1);
2253 } catch (InterruptedException discarded) {
2254 }
2255 close(progressIndicator);
2256 throw ex;
2257 }
2258 }
2259
2274 private Void open(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2275 assert (additionalParams == null);
2276 try {
2278 switchLoggingToCaseLogsDirectory(progressIndicator);
2280 updateCaseNodeData(progressIndicator);
2282 deleteTempfilesFromCaseDirectory(progressIndicator);
2284 openCaseDataBase(progressIndicator);
2286 openCaseLevelServices(progressIndicator);
2288 openAppServiceCaseResources(progressIndicator, false);
2290 openCommunicationChannels(progressIndicator);
2295 return null;
2296
2297 } catch (CaseActionException ex) {
2298 /*
2299 * Cancellation or failure. The sleep is a little hack to clear the
2300 * interrupted flag for this thread if this is a cancellation
2301 * scenario, so that the clean up can run to completion in the
2302 * current thread.
2303 */
2304 try {
2305 Thread.sleep(1);
2306 } catch (InterruptedException discarded) {
2307 }
2308 close(progressIndicator);
2309 throw ex;
2310 }
2311 }
2312
2317 @Messages({
2318 "# {0} - paths",
2319 "Case_checkImagePaths_noPaths=The following images had no associated paths: {0}",
2320 "Case_checkImagePaths_exceptionOccurred=An exception occurred while checking if image paths are present"
2321 })
2322 private void checkImagePaths() throws CaseActionException {
2323 // if there is a content provider, images don't necessarily need paths
2324 if (StringUtils.isNotBlank(this.metadata.getContentProviderName())) {
2325 return;
2326 }
2327
2328 // identify images without paths
2329 try {
2330 List<Image> noPathImages = new ArrayList<>();
2331 List<Image> images = this.caseDb.getImages();
2332 for (Image img: images) {
2333 if (ArrayUtils.isEmpty(img.getPaths())) {
2334 noPathImages.add(img);
2335 }
2336 }
2337
2338 if (!noPathImages.isEmpty()) {
2339 String imageListStr = noPathImages.stream().map(Image::getName).collect(Collectors.joining(", "));
2340 throw new CaseActionException(Bundle.Case_checkImagePaths_noPaths(imageListStr));
2341 }
2342 } catch (TskCoreException ex) {
2343 throw new CaseActionException(Bundle.Case_checkImagePaths_exceptionOccurred(), ex);
2344 }
2345 }
2346
2356 @Messages({
2357 "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...",
2358 "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..."
2359 })
2363 }
2364
2367 }
2368
2373 private static class BackgroundOpenFileSystemsTask implements Runnable {
2374
2375 private final SleuthkitCase tskCase;
2376 private final String caseName;
2377 private final long MAX_IMAGE_THRESHOLD = 100;
2379
2388 BackgroundOpenFileSystemsTask(SleuthkitCase tskCase, ProgressIndicator progressIndicator) {
2389 this.tskCase = tskCase;
2390 this.progressIndicator = progressIndicator;
2391 caseName = (this.tskCase != null) ? this.tskCase.getDatabaseName() : "";
2392 }
2393
2401 private void checkIfCancelled() throws InterruptedException {
2402 if (Thread.interrupted()) {
2403 throw new InterruptedException();
2404 }
2405 }
2406
2412 private List<Image> getImages() {
2413 progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName));
2414 try {
2415 return this.tskCase.getImages();
2416 } catch (TskCoreException ex) {
2417 logger.log(
2418 Level.SEVERE,
2419 String.format("Could not obtain images while opening case: %s.", caseName),
2420 ex);
2421
2422 return null;
2423 }
2424 }
2425
2435 private void openFileSystems(List<Image> images) throws TskCoreException, InterruptedException {
2436 byte[] tempBuff = new byte[512];
2437
2438 for (Image image : images) {
2439 String imageStr = image.getName();
2440
2441 progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr));
2442
2443 Collection<FileSystem> fileSystems = this.tskCase.getImageFileSystems(image);
2445 for (FileSystem fileSystem : fileSystems) {
2446 fileSystem.read(tempBuff, 0, 512);
2448 }
2449
2450 }
2451 }
2452
2453 @Override
2454 public void run() {
2455 try {
2457 List<Image> images = getImages();
2458 if (images == null) {
2459 return;
2460 }
2461
2462 if (images.size() > MAX_IMAGE_THRESHOLD) {
2463 // If we have a large number of images, don't try to preload anything
2464 logger.log(
2465 Level.INFO,
2466 String.format("Skipping background load of file systems due to large number of images in case (%d)", images.size()));
2467 return;
2468 }
2469
2471 openFileSystems(images);
2472 } catch (InterruptedException ex) {
2473 logger.log(
2474 Level.INFO,
2475 String.format("Background operation opening all file systems in %s has been cancelled.", caseName));
2476 } catch (Exception ex) {
2477 // Exception firewall
2478 logger.log(Level.WARNING, "Error while opening file systems in background", ex);
2479 }
2480 }
2481
2482 }
2483
2498 @Messages({
2499 "Case.progressMessage.deletingDataSource=Removing the data source from the case...",
2500 "Case.exceptionMessage.dataSourceNotFound=The data source was not found.",
2501 "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.",
2502 "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",})
2503 Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2504 assert (additionalParams instanceof Long);
2505 open(progressIndicator, null);
2506 try {
2507 progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource());
2508 Long dataSourceObjectID = (Long) additionalParams;
2509 try {
2510 DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID);
2511 if (dataSource == null) {
2512 throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound());
2513 }
2514 SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID);
2515 } catch (TskDataException | TskCoreException ex) {
2516 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex);
2517 }
2518 try {
2519 this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID);
2520 } catch (KeywordSearchServiceException ex) {
2521 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex);
2522 }
2523 eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID));
2524 return null;
2525 } finally {
2526 close(progressIndicator);
2528 }
2529 }
2530
2541 public SleuthkitCase createPortableCase(String caseName, File portableCaseFolder) throws TskCoreException {
2542
2543 if (portableCaseFolder.exists()) {
2544 throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
2545 }
2546 if (!portableCaseFolder.mkdirs()) {
2547 throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
2548 }
2549
2550 CaseDetails details = new CaseDetails(caseName, getNumber(), getExaminer(),
2552 try {
2553 CaseMetadata portableCaseMetadata = new CaseMetadata(Case.CaseType.SINGLE_USER_CASE, portableCaseFolder.toString(),
2554 caseName, details, metadata);
2555 portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2556 } catch (CaseMetadataException ex) {
2557 throw new TskCoreException("Error creating case metadata", ex);
2558 }
2559
2560 // Create the Sleuthkit case
2561 SleuthkitCase portableSleuthkitCase;
2562 String dbFilePath = Paths.get(portableCaseFolder.toString(), SINGLE_USER_CASE_DB_NAME).toString();
2563 portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
2564
2565 return portableSleuthkitCase;
2566 }
2567
2578 if (Thread.currentThread().isInterrupted()) {
2579 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2580 }
2581 }
2582
2593 @Messages({
2594 "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
2595 })
2597 /*
2598 * TODO (JIRA-2180): Always create the case directory as part of the
2599 * case creation process.
2600 */
2601 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2602 if (new File(metadata.getCaseDirectory()).exists() == false) {
2603 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2604 Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
2605 }
2606 }
2607
2614 @Messages({
2615 "Case.progressMessage.switchingLogDirectory=Switching log directory..."
2616 })
2617 private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
2618 progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2620 }
2621
2633 @Messages({
2634 "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2635 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2636 })
2637 private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
2638 progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
2639 try {
2640 this.metadata.writeToFile();
2641 } catch (CaseMetadataException ex) {
2642 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2643 }
2644 }
2645
2657 @Messages({
2658 "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2659 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2660 })
2661 private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2663 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2664 try {
2666 } catch (CaseNodeDataException | InterruptedException ex) {
2667 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2668 }
2669 }
2670 }
2671
2683 @Messages({
2684 "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2685 "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2686 })
2687 private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2689 progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2690 try {
2691 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2692 nodeData.setLastAccessDate(new Date());
2694 } catch (CaseNodeDataException | InterruptedException ex) {
2695 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2696 }
2697 }
2698 }
2699
2705 @Messages({
2706 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2707 })
2708 private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
2709 /*
2710 * Clear the temp subdirectory of the case directory.
2711 */
2712 progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2713 FileUtil.deleteDir(new File(this.getTempDirectory()));
2714 }
2715
2727 @Messages({
2728 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2729 "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2730 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2731 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2732 })
2733 private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
2734 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2735 try {
2736 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2737 /*
2738 * For single-user cases, the case database is a SQLite database
2739 * with a standard name, physically located in the case
2740 * directory.
2741 */
2742 caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString(), (ContentStreamProvider) null, APP_NAME);
2743 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2744 } else {
2745 /*
2746 * For multi-user cases, the case database is a PostgreSQL
2747 * database with a name derived from the case display name,
2748 * physically located on the PostgreSQL database server.
2749 */
2750 caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2751 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2752 }
2753 } catch (TskCoreException ex) {
2755 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2756 } catch (UserPreferencesException ex) {
2757 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2758 } catch (CaseMetadataException ex) {
2759 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2760 }
2761 }
2762
2774 @Messages({
2775 "Case.progressMessage.openingCaseDatabase=Opening case database...",
2776 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2777 "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2778 "Case.exceptionMessage.contentProviderCouldNotBeFound=Content provider was specified for the case but could not be loaded.",
2779 "# {0} - provider name, {1} - required version, {2} - installed version",
2780 "Case.versionMismatch.message=This case requires version {1} of the ''{0}'' plugin but version {2} is installed. Please upgrade the module to version {1} or a compatible version.",
2781 "Case.versionMismatch.title=Content Provider Version Mismatch",
2782 "Case.exceptionMessage.contentProviderVersionMismatch=The installed content provider plugin is not compatible with this case.",
2783 "# {0} - provider name", "Case.contentProviderNotFound.message=This case requires a content provider plugin (''{0}'') that is not installed. Please install the appropriate plugin.",
2784 "Case.contentProviderNotFound.title=Content Provider Not Found",
2785 "# {0} - provider name", "Case.contentProviderLoadFailed.message=The content provider plugin (''{0}'') is installed but could not be loaded. Check the module for errors.",
2786 "Case.contentProviderLoadFailed.title=Content Provider Load Failed",
2787 "Case.exceptionMessage.contentProviderLoadFailed=The content provider plugin is installed but failed to load.",
2788 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2789 })
2790 private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
2791 progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2792 try {
2793 String databaseName = metadata.getCaseDatabaseName();
2794
2795 ContentStreamProvider contentProvider = ContentProviderUtils.getContentProvider(metadata.getContentProviderName());
2796 if (StringUtils.isNotBlank(metadata.getContentProviderName()) && contentProvider == null) {
2797 String createdName = metadata.getContentProviderName().trim();
2798 Optional<AutopsyContentProvider> installedProvider = ContentProviderUtils.findInstalledProvider(createdName);
2799 if (installedProvider.isPresent()) {
2801 try {
2802 SwingUtilities.invokeAndWait(() -> JOptionPane.showMessageDialog(
2803 WindowManager.getDefault().getMainWindow(),
2804 Bundle.Case_contentProviderLoadFailed_message(createdName),
2805 Bundle.Case_contentProviderLoadFailed_title(),
2806 JOptionPane.ERROR_MESSAGE));
2807 } catch (InterruptedException | InvocationTargetException ex) {
2808 logger.log(Level.WARNING, "Error showing content provider load failed dialog", ex);
2809 }
2810 }
2811 throw new CaseActionException(Bundle.Case_exceptionMessage_contentProviderLoadFailed());
2812 } else {
2814 try {
2815 SwingUtilities.invokeAndWait(() -> JOptionPane.showMessageDialog(
2816 WindowManager.getDefault().getMainWindow(),
2817 Bundle.Case_contentProviderNotFound_message(createdName),
2818 Bundle.Case_contentProviderNotFound_title(),
2819 JOptionPane.ERROR_MESSAGE));
2820 } catch (InterruptedException | InvocationTargetException ex) {
2821 logger.log(Level.WARNING, "Error showing content provider not found dialog", ex);
2822 }
2823 }
2824 throw new CaseActionException(Bundle.Case_exceptionMessage_contentProviderCouldNotBeFound());
2825 }
2826 }
2827
2828 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2829 caseDb = SleuthkitCase.openCase(metadata.getCaseDatabasePath(), contentProvider, APP_NAME);
2831 caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory(), contentProvider);
2832 } else {
2833 throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2834 }
2836 } catch (TskUnsupportedSchemaVersionException ex) {
2837 throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2838 } catch (UserPreferencesException ex) {
2839 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2840 } catch (TskCoreException ex) {
2842 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2843 }
2844 }
2845
2846
2852 @Messages({
2853 "# {0} - appplicationName",
2854 "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.",
2855 "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp=another application"
2856 })
2858 ConcurrentDbAccessException concurrentEx = null;
2859 Throwable curEx = ex;
2860 // max depth search for a concurrent db access exception will be 10
2861 for (int i = 0; i < 10; i++) {
2862 if (curEx == null) {
2863 break;
2864 } else if (curEx instanceof ConcurrentDbAccessException foundEx) {
2865 concurrentEx = foundEx;
2866 break;
2867 } else {
2868 curEx = curEx.getCause();
2869 }
2870 }
2871
2872 if (concurrentEx != null) {
2873 throw new CaseActionException(Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException(
2874 StringUtils.defaultIfBlank(concurrentEx.getConflictingApplicationName(),
2875 Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp())
2876 ), concurrentEx);
2877 }
2878 }
2879
2880
2881
2882
2889 @Messages({
2890 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2891 private void openCaseLevelServices(ProgressIndicator progressIndicator) {
2892 progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2893 this.caseServices = new Services(caseDb);
2894 /*
2895 * RC Note: JM put this initialization here. I'm not sure why. However,
2896 * my attempt to put it in the openCaseDatabase method seems to lead to
2897 * intermittent unchecked exceptions concerning a missing subscriber.
2898 */
2899 caseDb.registerForEvents(sleuthkitEventListener);
2900 }
2901
2914 @NbBundle.Messages({
2915 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2916 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2917 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2918 "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2919 })
2920 private void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase) throws CaseActionException {
2921 /*
2922 * Each service gets its own independently cancellable/interruptible
2923 * task, running in a named thread managed by an executor service, with
2924 * its own progress indicator. This allows for cancellation of the
2925 * opening of case resources for individual services. It also makes it
2926 * possible to ensure that each service task completes before the next
2927 * one starts by awaiting termination of the executor service.
2928 */
2929 progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2930
2931 for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
2932 )) {
2933 /*
2934 * Create a progress indicator for the task and start the task. If
2935 * running with a GUI, the progress indicator will be a dialog box
2936 * with a Cancel button.
2937 */
2938 CancelButtonListener cancelButtonListener = null;
2939 ProgressIndicator appServiceProgressIndicator;
2941 cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2942 appServiceProgressIndicator = new ModalDialogProgressIndicator(
2943 mainFrame,
2944 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2945 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2946 Bundle.Case_progressIndicatorCancelButton_label(),
2947 cancelButtonListener);
2948 } else {
2949 appServiceProgressIndicator = new LoggingProgressIndicator();
2950 }
2951 appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
2952 AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator, isNewCase);
2953 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2954 threadNameSuffix = threadNameSuffix.toLowerCase();
2955 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2956 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2957 Future<Void> future = executor.submit(() -> {
2958 service.openCaseResources(context);
2959 return null;
2960 });
2961 if (null != cancelButtonListener) {
2962 cancelButtonListener.setCaseContext(context);
2963 cancelButtonListener.setCaseActionFuture(future);
2964 }
2965
2966 /*
2967 * Wait for the task to either be completed or
2968 * cancelled/interrupted, or for the opening of the case to be
2969 * cancelled.
2970 */
2971 try {
2972 future.get();
2973 } catch (InterruptedException discarded) {
2974 /*
2975 * The parent create/open case task has been cancelled.
2976 */
2977 Case.logger.log(Level.WARNING, String.format("Opening of %s (%s) in %s cancelled during opening of case resources by %s", getDisplayName(), getName(), getCaseDirectory(), service.getServiceName()));
2978 future.cancel(true);
2979 } catch (CancellationException discarded) {
2980 /*
2981 * The opening of case resources by the application service has
2982 * been cancelled, so the executor service has thrown. Note that
2983 * there is no guarantee the task itself has responded to the
2984 * cancellation request yet.
2985 */
2986 Case.logger.log(Level.WARNING, String.format("Opening of case resources by %s for %s (%s) in %s cancelled", service.getServiceName(), getDisplayName(), getName(), getCaseDirectory(), service.getServiceName()));
2987 } catch (ExecutionException ex) {
2988 /*
2989 * An exception was thrown while executing the task. The
2990 * case-specific application service resources are not
2991 * essential. Log an error and notify the user if running the
2992 * desktop GUI, but do not throw.
2993 */
2994 Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2996 SwingUtilities.invokeLater(() -> {
2997 MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2998 });
2999 }
3000 } finally {
3001 /*
3002 * Shut down the executor service and wait for it to finish.
3003 * This ensures that the task has finished. Without this, it
3004 * would be possible to start the next task before the current
3005 * task responded to a cancellation request.
3006 */
3008 appServiceProgressIndicator.finish();
3009 }
3011 }
3012 }
3013
3025 @Messages({
3026 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
3027 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
3028 "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
3029 })
3030 private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
3031 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
3032 progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
3033 try {
3034 eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
3036 collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
3037 } catch (AutopsyEventException ex) {
3038 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
3039 } catch (CollaborationMonitor.CollaborationMonitorException ex) {
3040 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
3041 }
3042 }
3043 }
3044
3060 /*
3061 * Set up either a GUI progress indicator without a Cancel button or a
3062 * logging progress indicator.
3063 */
3064 ProgressIndicator progressIndicator;
3066 progressIndicator = new ModalDialogProgressIndicator(
3067 mainFrame,
3068 Bundle.Case_progressIndicatorTitle_closingCase());
3069 } else {
3070 progressIndicator = new LoggingProgressIndicator();
3071 }
3072 progressIndicator.start(Bundle.Case_progressMessage_preparing());
3073
3074 /*
3075 * Closing a case is always done in the same non-UI thread that
3076 * opened/created the case. If the case is a multi-user case, this
3077 * ensures that case lock that is held as long as the case is open is
3078 * released in the same thread in which it was acquired, as is required
3079 * by the coordination service.
3080 */
3081 Future<Void> future = caseActionExecutor.submit(() -> {
3082 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
3083 close(progressIndicator);
3084 } else {
3085 /*
3086 * Acquire an exclusive case resources lock to ensure only one
3087 * node at a time can create/open/upgrade/close the case
3088 * resources.
3089 */
3090 progressIndicator.progress(Bundle.Case_progressMessage_preparing());
3091 try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
3092 if (null == resourcesLock) {
3093 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
3094 }
3095 close(progressIndicator);
3096 } finally {
3097 /*
3098 * Always release the case directory lock that was acquired
3099 * when the case was opened.
3100 */
3102 }
3103 }
3104 return null;
3105 });
3106
3107 try {
3108 future.get();
3109 } catch (InterruptedException | CancellationException unused) {
3110 /*
3111 * The wait has been interrupted by interrupting the thread running
3112 * this method. Not allowing cancellation of case closing, so ignore
3113 * the interrupt. Likewise, cancellation of the case closing task is
3114 * not supported.
3115 */
3116 } catch (ExecutionException ex) {
3117 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
3118 } finally {
3120 progressIndicator.finish();
3121 }
3122 }
3123
3129 @Messages({
3130 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
3131 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
3132 "Case.progressMessage.closingCaseDatabase=Closing case database..."
3133 })
3134 private void close(ProgressIndicator progressIndicator) {
3136
3137 /*
3138 * Stop sending/receiving case events to and from other nodes if this is
3139 * a multi-user case.
3140 */
3141 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
3142 progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
3143 if (null != collaborationMonitor) {
3144 collaborationMonitor.shutdown();
3145 }
3146 eventPublisher.closeRemoteEventChannel();
3147 }
3148
3149 /*
3150 * Allow all registered application services providers to close
3151 * resources related to the case.
3152 */
3153 progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
3155
3156 /*
3157 * Close the case database.
3158 */
3159 if (null != caseDb) {
3160 progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
3161 caseDb.unregisterForEvents(sleuthkitEventListener);
3162 caseDb.close();
3163 }
3164
3168 deleteTempfilesFromCaseDirectory(progressIndicator);
3169
3170 /*
3171 * Switch the log directory.
3172 */
3173 progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
3175 }
3176
3181 @Messages({
3182 "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
3183 "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
3184 })
3186 /*
3187 * Each service gets its own independently cancellable task, and thus
3188 * its own task progress indicator.
3189 */
3190 for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
3191 )) {
3192 ProgressIndicator progressIndicator;
3194 progressIndicator = new ModalDialogProgressIndicator(
3195 mainFrame,
3196 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
3197 } else {
3198 progressIndicator = new LoggingProgressIndicator();
3199 }
3200 progressIndicator.start(Bundle.Case_progressMessage_preparing());
3201 AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
3202 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
3203 threadNameSuffix = threadNameSuffix.toLowerCase();
3204 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
3205 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
3206 Future<Void> future = executor.submit(() -> {
3207 service.closeCaseResources(context);
3208 return null;
3209 });
3210 try {
3211 future.get();
3212 } catch (InterruptedException ex) {
3213 Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
3214 } catch (CancellationException ex) {
3215 Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
3216 } catch (ExecutionException ex) {
3217 Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
3219 SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
3220 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
3221 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
3222 }
3223 } finally {
3225 progressIndicator.finish();
3226 }
3227 }
3228 }
3229
3235 @Messages({
3236 "Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.",
3237 "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case."
3238 })
3239 private void acquireCaseLock(CaseLockType lockType) throws CaseActionException {
3240 String caseDir = metadata.getCaseDirectory();
3241 try {
3242 CoordinationService coordinationService = CoordinationService.getInstance();
3243 caseLock = lockType == CaseLockType.SHARED
3244 ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES)
3245 : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES);
3246 if (caseLock == null) {
3247 if (lockType == CaseLockType.SHARED) {
3248 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock());
3249 } else {
3250 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock());
3251 }
3252 }
3253 } catch (InterruptedException | CoordinationServiceException ex) {
3254 if (lockType == CaseLockType.SHARED) {
3255 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex);
3256 } else {
3257 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex);
3258 }
3259 }
3260 }
3261
3265 private void releaseCaseLock() {
3266 if (caseLock != null) {
3267 try {
3268 caseLock.release();
3269 caseLock = null;
3271 logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", getMetadata().getCaseDirectory()), ex);
3272 }
3273 }
3274 }
3275
3282 private String getOrCreateSubdirectory(String subDirectoryName) {
3283 File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
3284 if (!subDirectory.exists()) {
3285 subDirectory.mkdirs();
3286 }
3287 return subDirectory.toString();
3288
3289 }
3290
3302 @Messages({
3303 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
3304 })
3305 private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
3306 boolean errorsOccurred = false;
3307 try {
3308 deleteTextIndex(metadata, progressIndicator);
3309 } catch (KeywordSearchServiceException ex) {
3310 errorsOccurred = true;
3311 logger.log(Level.WARNING, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3312 }
3313
3314 try {
3315 deleteCaseDirectory(metadata, progressIndicator);
3316 } catch (CaseActionException ex) {
3317 errorsOccurred = true;
3318 logger.log(Level.WARNING, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3319 }
3320
3321 deleteFromRecentCases(metadata, progressIndicator);
3322
3323 if (errorsOccurred) {
3324 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
3325 }
3326 }
3327
3347 @Messages({
3348 "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
3349 "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
3350 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
3351 "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
3352 "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
3353 "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
3354 "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
3355 "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
3356 })
3357 private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
3358 progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
3359 CoordinationService coordinationService;
3360 try {
3361 coordinationService = CoordinationService.getInstance();
3362 } catch (CoordinationServiceException ex) {
3363 logger.log(Level.SEVERE, String.format("Failed to connect to coordination service when attempting to delete %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3364 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
3365 }
3366
3367 CaseNodeData caseNodeData;
3368 boolean errorsOccurred = false;
3369 try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
3370 if (dirLock == null) {
3371 logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case directory lock was held by another host", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory())); //NON-NLS
3372 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
3373 }
3374
3375 progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
3376 try {
3377 caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
3378 } catch (CaseNodeDataException | InterruptedException ex) {
3379 logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3380 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
3381 }
3382
3383 errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator, logger);
3384
3385 progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
3386 try {
3387 String resourcesLockNodePath = CoordinationServiceUtils.getCaseResourcesNodePath(caseNodeData.getDirectory());
3388 coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
3389 } catch (CoordinationServiceException ex) {
3390 if (!isNoNodeException(ex)) {
3391 errorsOccurred = true;
3392 logger.log(Level.WARNING, String.format("Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3393 }
3394 } catch (InterruptedException ex) {
3395 logger.log(Level.WARNING, String.format("Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3396 }
3397
3398 } catch (CoordinationServiceException ex) {
3399 logger.log(Level.SEVERE, String.format("Error exclusively locking the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3400 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
3401 }
3402
3403 if (!errorsOccurred) {
3404 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
3405 try {
3406 String casDirNodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseNodeData.getDirectory());
3407 coordinationService.deleteNode(CategoryNode.CASES, casDirNodePath);
3408 } catch (CoordinationServiceException | InterruptedException ex) {
3409 logger.log(Level.SEVERE, String.format("Error deleting the case directory lock node for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3410 errorsOccurred = true;
3411 }
3412 }
3413
3414 if (errorsOccurred) {
3415 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
3416 }
3417 }
3418
3443 @Beta
3444 public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws InterruptedException {
3445 boolean errorsOccurred = false;
3446 try {
3447 deleteMultiUserCaseDatabase(caseNodeData, metadata, progressIndicator, logger);
3448 deleteMultiUserCaseTextIndex(caseNodeData, metadata, progressIndicator, logger);
3449 deleteMultiUserCaseDirectory(caseNodeData, metadata, progressIndicator, logger);
3450 deleteFromRecentCases(metadata, progressIndicator);
3451 } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
3452 errorsOccurred = true;
3453 logger.log(Level.WARNING, String.format("Failed to delete the case database for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3454 } catch (KeywordSearchServiceException ex) {
3455 errorsOccurred = true;
3456 logger.log(Level.WARNING, String.format("Failed to delete the text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3457 } catch (CaseActionException ex) {
3458 errorsOccurred = true;
3459 logger.log(Level.WARNING, String.format("Failed to delete the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
3460 }
3461 return errorsOccurred;
3462 }
3463
3484 @Messages({
3485 "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
3486 })
3487 private static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
3489 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
3490 logger.log(Level.INFO, String.format("Deleting case database for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3491 CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
3492 String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
3493 Class.forName("org.postgresql.Driver"); //NON-NLS
3494 try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
3495 String dbExistsQuery = "SELECT 1 from pg_database WHERE datname = '" + metadata.getCaseDatabaseName() + "'"; //NON-NLS
3496 try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
3497 if (queryResult.next()) {
3498 String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
3499 statement.execute(deleteCommand);
3500 }
3501 }
3502 }
3504 }
3505 }
3506
3522 private static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws KeywordSearchServiceException, InterruptedException {
3524 logger.log(Level.INFO, String.format("Deleting text index for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3525 deleteTextIndex(metadata, progressIndicator);
3527 }
3528 }
3529
3539 @Messages({
3540 "Case.progressMessage.deletingTextIndex=Deleting text index..."
3541 })
3543 progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
3544
3545 for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class
3546 )) {
3547 searchService.deleteTextIndex(metadata);
3548 }
3549 }
3550
3565 private static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws CaseActionException, InterruptedException {
3567 logger.log(Level.INFO, String.format("Deleting case directory for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3568 deleteCaseDirectory(metadata, progressIndicator);
3570 }
3571 }
3572
3582 @Messages({
3583 "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
3584 })
3585 private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
3586 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
3587 if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
3588 throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory())); //NON-NLS
3589 }
3590 }
3591
3599 @Messages({
3600 "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
3601 })
3602 private static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator) {
3604 progressIndicator.progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
3605 SwingUtilities.invokeLater(() -> {
3606 RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
3607 });
3608 }
3609 }
3610
3621 boolean isNodeNodeEx = false;
3622 Throwable cause = ex.getCause();
3623 if (cause != null) {
3624 String causeMessage = cause.getMessage();
3625 isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
3626 }
3627 return isNodeNodeEx;
3628 }
3629
3641 private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
3642 try {
3643 caseNodeData.setDeletedFlag(flag);
3644 CaseNodeData.writeCaseNodeData(caseNodeData);
3645 } catch (CaseNodeDataException ex) {
3646 logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
3647
3648 }
3649 }
3650
3662 private void updateDataParameters() throws TskCoreException {
3664
3665 if (!hasDataSource) {
3666 hasData = dbHasData();
3667 } else {
3668 hasData = true;
3669 }
3670 }
3671
3679 private boolean dbHasDataSource() throws TskCoreException {
3680 String query = "SELECT count(*) AS count FROM (SELECT * FROM data_source_info LIMIT 1)t";
3681 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
3682 ResultSet resultSet = dbQuery.getResultSet();
3683 if (resultSet.next()) {
3684 return resultSet.getLong("count") > 0;
3685 }
3686 return false;
3687 } catch (SQLException ex) {
3688 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
3689 throw new TskCoreException("Error accessing case databse", ex);
3690 }
3691 }
3692
3701 private boolean dbHasData() throws TskCoreException {
3702 // The LIMIT 1 in the subquery should limit the data returned and
3703 // make the overall query more efficent.
3704 String query = "SELECT SUM(cnt) total FROM "
3705 + "(SELECT COUNT(*) AS cnt FROM "
3706 + "(SELECT * FROM tsk_objects LIMIT 1)t "
3707 + "UNION ALL "
3708 + "SELECT COUNT(*) AS cnt FROM "
3709 + "(SELECT * FROM tsk_hosts LIMIT 1)r) s";
3710 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
3711 ResultSet resultSet = dbQuery.getResultSet();
3712 if (resultSet.next()) {
3713 return resultSet.getLong("total") > 0;
3714 } else {
3715 return false;
3716 }
3717 } catch (SQLException ex) {
3718 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
3719 throw new TskCoreException("Error accessing case databse", ex);
3720 }
3721 }
3722
3731 private interface CaseAction<T, V, R> {
3732
3743 R execute(T progressIndicator, V additionalParams) throws CaseActionException;
3744 }
3745
3750 private enum CaseLockType {
3752 }
3753
3758 @ThreadSafe
3759 private final static class CancelButtonListener implements ActionListener {
3760
3761 private final String cancellationMessage;
3762 @GuardedBy("this")
3763 private boolean cancelRequested;
3764 @GuardedBy("this")
3766 @GuardedBy("this")
3767 private Future<?> caseActionFuture;
3768
3778 this.cancellationMessage = cancellationMessage;
3779 }
3780
3786 private synchronized void setCaseContext(CaseContext caseContext) {
3787 this.caseContext = caseContext;
3788 /*
3789 * If the cancel button has already been pressed, pass the
3790 * cancellation on to the case context.
3791 */
3792 if (cancelRequested) {
3793 cancel();
3794 }
3795 }
3796
3802 private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
3803 this.caseActionFuture = caseActionFuture;
3804 /*
3805 * If the cancel button has already been pressed, cancel the Future
3806 * of the task.
3807 */
3808 if (cancelRequested) {
3809 cancel();
3810 }
3811 }
3812
3818 @Override
3819 public synchronized void actionPerformed(ActionEvent event) {
3820 cancel();
3821 }
3822
3826 private void cancel() {
3827 /*
3828 * At a minimum, set the cancellation requested flag of this
3829 * listener.
3830 */
3831 this.cancelRequested = true;
3832 if (null != this.caseContext) {
3833 /*
3834 * Set the cancellation request flag and display the
3835 * cancellation message in the progress indicator for the case
3836 * context associated with this listener.
3837 */
3839 ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
3840 if (progressIndicator instanceof ModalDialogProgressIndicator) {
3841 ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
3842 }
3843 }
3844 this.caseContext.requestCancel();
3845 }
3846 if (null != this.caseActionFuture) {
3847 /*
3848 * Cancel the Future of the task associated with this listener.
3849 * Note that the task thread will be interrupted if the task is
3850 * blocked.
3851 */
3852 this.caseActionFuture.cancel(true);
3853 }
3854 }
3855 }
3856
3860 private static class TaskThreadFactory implements ThreadFactory {
3861
3862 private final String threadName;
3863
3865 this.threadName = threadName;
3866 }
3867
3868 @Override
3869 public Thread newThread(Runnable task) {
3870 return new Thread(task, threadName);
3871 }
3872
3873 }
3874
3882 @Deprecated
3883 public static String getAppName() {
3884 return UserPreferences.getAppName();
3885 }
3886
3906 @Deprecated
3907 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
3908 createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
3909 }
3910
3931 @Deprecated
3932 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
3933 createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
3934 }
3935
3947 @Deprecated
3948 public static void open(String caseMetadataFilePath) throws CaseActionException {
3949 openAsCurrentCase(caseMetadataFilePath);
3950 }
3951
3961 @Deprecated
3962 public void closeCase() throws CaseActionException {
3964 }
3965
3971 @Deprecated
3972 public static void invokeStartupDialog() {
3974 }
3975
3989 @Deprecated
3990 public static String convertTimeZone(String timeZoneId) {
3991 return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
3992 }
3993
4003 @Deprecated
4004 public static boolean pathExists(String filePath) {
4005 return new File(filePath).isFile();
4006 }
4007
4016 @Deprecated
4017 public static String getAutopsyVersion() {
4018 return Version.getVersion();
4019 }
4020
4028 @Deprecated
4029 public static boolean existsCurrentCase() {
4030 return isCaseOpen();
4031 }
4032
4042 @Deprecated
4043 public static String getModulesOutputDirRelPath() {
4044 return "ModuleOutput"; //NON-NLS
4045 }
4046
4056 @Deprecated
4057 public static PropertyChangeSupport
4059 return new PropertyChangeSupport(Case.class
4060 );
4061 }
4062
4071 @Deprecated
4073 return getModuleDirectory();
4074 }
4075
4090 @Deprecated
4091 public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
4092 try {
4093 Image newDataSource = caseDb.getImageById(imgId);
4094 notifyDataSourceAdded(newDataSource, UUID.randomUUID());
4095 return newDataSource;
4096 } catch (TskCoreException ex) {
4097 throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
4098 }
4099 }
4100
4108 @Deprecated
4109 public Set<TimeZone> getTimeZone() {
4110 return getTimeZones();
4111 }
4112
4123 @Deprecated
4124 public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
4125 deleteReports(reports);
4126 }
4127
4128}
synchronized void setCaseContext(CaseContext caseContext)
Definition Case.java:3786
synchronized void actionPerformed(ActionEvent event)
Definition Case.java:3819
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition Case.java:3802
void publishOsAccountInstancesAddedEvent(TskEvent.OsAcctInstancesAddedTskEvent event)
Definition Case.java:555
void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event)
Definition Case.java:566
void publicTagNamesDeleted(TskEvent.TagNamesDeletedTskEvent event)
Definition Case.java:653
void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event)
Definition Case.java:540
void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event)
Definition Case.java:633
void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event)
Definition Case.java:529
void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event)
Definition Case.java:628
void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event)
Definition Case.java:534
void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event)
Definition Case.java:589
void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event)
Definition Case.java:617
void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event)
Definition Case.java:606
void publicTagSetsDeleted(TskEvent.TagSetsDeletedTskEvent event)
Definition Case.java:663
void publicTagSetsAdded(TskEvent.TagSetsAddedTskEvent event)
Definition Case.java:658
void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event)
Definition Case.java:578
void publicTagNamesAdded(TskEvent.TagNamesAddedTskEvent event)
Definition Case.java:643
void publicTagNamesUpdated(TskEvent.TagNamesUpdatedTskEvent event)
Definition Case.java:648
void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event)
Definition Case.java:638
void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event)
Definition Case.java:545
static boolean pathExists(String filePath)
Definition Case.java:4004
static void openAsCurrentCase(String caseMetadataFilePath)
Definition Case.java:855
static final String EVENT_CHANNEL_NAME
Definition Case.java:175
void notifyContentTagAdded(ContentTag newTag, List< ContentTag > deletedTagList)
Definition Case.java:1837
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:756
String getOrCreateSubdirectory(String subDirectoryName)
Definition Case.java:3282
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3357
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition Case.java:1785
static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS
Definition Case.java:171
static String displayNameToUniqueName(String caseDisplayName)
Definition Case.java:1130
Case(CaseMetadata caseMetaData)
Definition Case.java:2079
void doOpenCaseAction(String progressIndicatorTitle, CaseAction< ProgressIndicator, Object, Void > caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams)
Definition Case.java:2120
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3565
Void open(ProgressIndicator progressIndicator, Object additionalParams)
Definition Case.java:2274
void deleteReports(Collection<? extends Report > reports, boolean deleteFromDisk)
Definition Case.java:4124
static final String MODULE_FOLDER
Definition Case.java:181
static volatile Case currentCase
Definition Case.java:192
volatile ExecutorService caseActionExecutor
Definition Case.java:194
static final String CASE_RESOURCES_THREAD_NAME
Definition Case.java:183
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
Definition Case.java:3641
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition Case.java:701
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3522
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition Case.java:2070
static Future<?> backgroundOpenFileSystemsFuture
Definition Case.java:188
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition Case.java:1288
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3602
static final String TEMP_FOLDER
Definition Case.java:173
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition Case.java:2541
void updateCaseNodeData(ProgressIndicator progressIndicator)
Definition Case.java:2687
static final String EXPORT_FOLDER
Definition Case.java:177
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition Case.java:726
static final String SINGLE_USER_CASE_DB_NAME
Definition Case.java:174
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition Case.java:736
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition Case.java:1081
static void open(String caseMetadataFilePath)
Definition Case.java:3948
void createCaseDatabase(ProgressIndicator progressIndicator)
Definition Case.java:2733
void notifyContentTagDeleted(ContentTag deletedTag)
Definition Case.java:1848
static final String CACHE_FOLDER
Definition Case.java:176
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition Case.java:686
void notifyDataSourceNameChanged(Content dataSource, String newName)
Definition Case.java:1813
void notifyAddingDataSource(UUID eventId)
Definition Case.java:1769
void createCaseNodeData(ProgressIndicator progressIndicator)
Definition Case.java:2661
static boolean isValidName(String caseName)
Definition Case.java:770
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition Case.java:1946
static final String NO_NODE_ERROR_MSG_FRAGMENT
Definition Case.java:184
static final Object caseActionSerializationLock
Definition Case.java:187
static final String REPORTS_FOLDER
Definition Case.java:179
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
Definition Case.java:2596
void addReport(String localPath, String srcModuleName, String reportName)
Definition Case.java:1928
static String getModulesOutputDirRelPath()
Definition Case.java:4043
static void createCaseDirectory(String caseDirPath, CaseType caseType)
Definition Case.java:1170
final SleuthkitEventListener sleuthkitEventListener
Definition Case.java:197
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition Case.java:746
void openCommunicationChannels(ProgressIndicator progressIndicator)
Definition Case.java:3030
static final AutopsyEventPublisher eventPublisher
Definition Case.java:186
static final ExecutorService openFileSystemsExecutor
Definition Case.java:190
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition Case.java:801
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition Case.java:674
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition Case.java:3907
static String convertTimeZone(String timeZoneId)
Definition Case.java:3990
static final String LOG_FOLDER
Definition Case.java:178
void close(ProgressIndicator progressIndicator)
Definition Case.java:3134
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3542
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List< BlackboardArtifactTag > removedTagList)
Definition Case.java:1902
static final String CONFIG_FOLDER
Definition Case.java:180
void notifyTagDefinitionChanged(String changedTagName)
Definition Case.java:1859
static void deleteCase(CaseMetadata metadata)
Definition Case.java:1032
static CoordinationService.Lock acquireCaseResourcesLock(String caseDir)
Definition Case.java:1262
static PropertyChangeSupport getPropertyChangeSupport()
Definition Case.java:4058
static volatile Frame mainFrame
Definition Case.java:191
void deleteReports(Collection<? extends Report > reports)
Definition Case.java:1985
void notifyCentralRepoCommentChanged(long contentId, String newComment)
Definition Case.java:1874
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition Case.java:3932
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3487
void notifyContentTagAdded(ContentTag newTag)
Definition Case.java:1824
void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase)
Definition Case.java:2920
static final String CASE_ACTION_THREAD_NAME
Definition Case.java:182
static final int CASE_LOCK_TIMEOUT_MINS
Definition Case.java:170
void throwIfConcurrentDbAccessException(Exception ex)
Definition Case.java:2857
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
Definition Case.java:2637
void acquireCaseLock(CaseLockType lockType)
Definition Case.java:3239
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3305
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition Case.java:828
static boolean isNoNodeException(CoordinationServiceException ex)
Definition Case.java:3620
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3444
CollaborationMonitor collaborationMonitor
Definition Case.java:198
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition Case.java:1889
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
Definition Case.java:2617
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
Definition Case.java:2708
Image addImage(String imgPath, long imgId, String timeZone)
Definition Case.java:4091
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition Case.java:1800
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3585
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition Case.java:1913
void openCaseDataBase(ProgressIndicator progressIndicator)
Definition Case.java:2790
void openCaseLevelServices(ProgressIndicator progressIndicator)
Definition Case.java:2891
CoordinationService.Lock caseLock
Definition Case.java:195
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:711
Void create(ProgressIndicator progressIndicator, Object additionalParams)
Definition Case.java:2222
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static boolean driveExists(String path)
static boolean deleteDir(File dirPath)
Definition FileUtil.java:47
synchronized static Logger getLogger(String name)
Definition Logger.java:124
synchronized static void setLogDirectory(String directoryPath)
Definition Logger.java:89
static void shutDownTaskExecutor(ExecutorService executor)
static String convertToAlphaNumericFormat(String timeZoneId)
static synchronized IngestManager getInstance()
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static CaseType fromString(String typeName)
Definition Case.java:234
boolean equalsName(String otherTypeName)
Definition Case.java:292
R execute(T progressIndicator, V additionalParams)
void start(String message, int totalWorkUnits)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.