Autopsy 4.22.1
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 com.basistech.df.cybertriage.autopsy.CTIntegrationMissingDialog;
22import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
23import com.google.common.annotations.Beta;
24import com.google.common.eventbus.Subscribe;
25import com.google.common.util.concurrent.ThreadFactoryBuilder;
26import java.awt.Cursor;
27import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
28import java.awt.Frame;
29import java.awt.GraphicsEnvironment;
30import java.awt.event.ActionEvent;
31import java.awt.event.ActionListener;
32import java.beans.PropertyChangeListener;
33import java.beans.PropertyChangeSupport;
34import java.io.File;
35import java.lang.reflect.InvocationTargetException;
36import java.nio.file.InvalidPathException;
37import java.nio.file.Path;
38import java.nio.file.Paths;
39import java.sql.Connection;
40import java.sql.DriverManager;
41import java.sql.ResultSet;
42import java.sql.SQLException;
43import java.sql.Statement;
44import java.text.SimpleDateFormat;
45import java.util.ArrayList;
46import java.util.Collection;
47import java.util.Date;
48import java.util.HashMap;
49import java.util.HashSet;
50import java.util.List;
51import java.util.Map;
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 String CT_PROVIDER_PREFIX = "CTStandardContentProvider_";
186 private static final Logger logger = Logger.getLogger(Case.class.getName());
188 private static final Object caseActionSerializationLock = new Object();
189 private static Future<?> backgroundOpenFileSystemsFuture = null;
190 private static final ExecutorService openFileSystemsExecutor
191 = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("case-open-file-systems-%d").build());
192 private static volatile Frame mainFrame;
193 private static volatile Case currentCase;
194 private final CaseMetadata metadata;
195 private volatile ExecutorService caseActionExecutor;
197 private SleuthkitCase caseDb;
199 private CollaborationMonitor collaborationMonitor;
201
202 private volatile boolean hasDataSource = false;
203 private volatile boolean hasData = false;
204
205 /*
206 * Get a reference to the main window of the desktop application to use to
207 * parent pop up dialogs and initialize the application name for use in
208 * changing the main window title.
209 */
210 static {
211 if (!GraphicsEnvironment.isHeadless()) {
212 WindowManager.getDefault().invokeWhenUIReady(() -> {
213 mainFrame = WindowManager.getDefault().getMainWindow();
214 });
215 }
216 }
217
221 public enum CaseType {
222
223 SINGLE_USER_CASE("Single-user case"), //NON-NLS
224 MULTI_USER_CASE("Multi-user case"); //NON-NLS
225
226 private final String typeName;
227
235 public static CaseType fromString(String typeName) {
236 if (typeName != null) {
237 for (CaseType c : CaseType.values()) {
238 if (typeName.equalsIgnoreCase(c.toString())) {
239 return c;
240 }
241 }
242 }
243 return null;
244 }
245
251 @Override
252 public String toString() {
253 return typeName;
254 }
255
261 @Messages({
262 "Case_caseType_singleUser=Single-user case",
263 "Case_caseType_multiUser=Multi-user case"
264 })
267 return Bundle.Case_caseType_singleUser();
268 } else {
269 return Bundle.Case_caseType_multiUser();
270 }
271 }
272
278 private CaseType(String typeName) {
279 this.typeName = typeName;
280 }
281
292 @Deprecated
293 public boolean equalsName(String otherTypeName) {
294 return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
295 }
296
297 };
298
521
527 private final class SleuthkitEventListener {
528
529 @Subscribe
530 public void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event) {
531 eventPublisher.publish(new TimelineEventAddedEvent(event));
532 }
533
534 @Subscribe
535 public void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
536 hasData = true;
537 eventPublisher.publish(new OsAccountsAddedEvent(event.getOsAcounts()));
538 }
539
540 @Subscribe
541 public void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event) {
542 eventPublisher.publish(new OsAccountsUpdatedEvent(event.getOsAcounts()));
543 }
544
545 @Subscribe
546 public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
547 try {
548 hasData = dbHasData();
549 } catch (TskCoreException ex) {
550 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
551 }
552 eventPublisher.publish(new OsAccountsDeletedEvent(event.getOsAccountObjectIds()));
553 }
554
555 @Subscribe
556 public void publishOsAccountInstancesAddedEvent(TskEvent.OsAcctInstancesAddedTskEvent event) {
557 eventPublisher.publish(new OsAcctInstancesAddedEvent(event.getOsAccountInstances()));
558 }
559
566 @Subscribe
567 public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
568 hasData = true;
569 eventPublisher.publish(new HostsAddedEvent(event.getHosts()));
570 }
571
578 @Subscribe
579 public void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event) {
580 eventPublisher.publish(new HostsUpdatedEvent(event.getHosts()));
581 }
582
589 @Subscribe
590 public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
591 try {
592 hasData = dbHasData();
593 } catch (TskCoreException ex) {
594 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
595 }
596
597 eventPublisher.publish(new HostsDeletedEvent(event.getHostIds()));
598 }
599
606 @Subscribe
607 public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
608 eventPublisher.publish(new PersonsAddedEvent(event.getPersons()));
609 }
610
617 @Subscribe
618 public void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event) {
619 eventPublisher.publish(new PersonsUpdatedEvent(event.getPersons()));
620 }
621
628 @Subscribe
629 public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
630 eventPublisher.publish(new PersonsDeletedEvent(event.getPersonIds()));
631 }
632
633 @Subscribe
634 public void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event) {
635 eventPublisher.publish(new HostsAddedToPersonEvent(event.getPerson(), event.getHosts()));
636 }
637
638 @Subscribe
639 public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) {
640 eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHostIds()));
641 }
642
643 @Subscribe
644 public void publicTagNamesAdded(TskEvent.TagNamesAddedTskEvent event) {
645 eventPublisher.publish(new TagNamesAddedEvent(event.getTagNames()));
646 }
647
648 @Subscribe
649 public void publicTagNamesUpdated(TskEvent.TagNamesUpdatedTskEvent event) {
650 eventPublisher.publish(new TagNamesUpdatedEvent(event.getTagNames()));
651 }
652
653 @Subscribe
654 public void publicTagNamesDeleted(TskEvent.TagNamesDeletedTskEvent event) {
655 eventPublisher.publish(new TagNamesDeletedEvent(event.getTagNameIds()));
656 }
657
658 @Subscribe
659 public void publicTagSetsAdded(TskEvent.TagSetsAddedTskEvent event) {
660 eventPublisher.publish(new TagSetsAddedEvent(event.getTagSets()));
661 }
662
663 @Subscribe
664 public void publicTagSetsDeleted(TskEvent.TagSetsDeletedTskEvent event) {
665 eventPublisher.publish(new TagSetsDeletedEvent(event.getTagSetIds()));
666 }
667 }
668
675 public static void addPropertyChangeListener(PropertyChangeListener listener) {
676 addEventSubscriber(Stream.of(Events.values())
677 .map(Events::toString)
678 .collect(Collectors.toSet()), listener);
679 }
680
687 public static void removePropertyChangeListener(PropertyChangeListener listener) {
688 removeEventSubscriber(Stream.of(Events.values())
689 .map(Events::toString)
690 .collect(Collectors.toSet()), listener);
691 }
692
701 @Deprecated
702 public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
703 eventPublisher.addSubscriber(eventNames, subscriber);
704 }
705
712 public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
713 eventTypes.forEach((Events event) -> {
714 eventPublisher.addSubscriber(event.toString(), subscriber);
715 });
716 }
717
726 @Deprecated
727 public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
728 eventPublisher.addSubscriber(eventName, subscriber);
729 }
730
737 public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
738 eventPublisher.removeSubscriber(eventName, subscriber);
739 }
740
747 public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
748 eventPublisher.removeSubscriber(eventNames, subscriber);
749 }
750
757 public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
758 eventTypes.forEach((Events event) -> {
759 eventPublisher.removeSubscriber(event.toString(), subscriber);
760 });
761 }
762
771 public static boolean isValidName(String caseName) {
772 return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
773 || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
774 || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
775 }
776
801 @Deprecated
802 public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
803 createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
804 }
805
825 @Messages({
826 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
827 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
828 })
829 public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
830 if (caseDetails.getCaseDisplayName().isEmpty()) {
831 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
832 }
833 if (caseDir.isEmpty()) {
834 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
835 }
836 openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
837 }
838
852 @Messages({
853 "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
854 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
855 })
856 public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
858 try {
859 metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
860 } catch (CaseMetadataException ex) {
861 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
862 }
864 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
865 }
866 openAsCurrentCase(new Case(metadata), false);
867 }
868
874 public static boolean isCaseOpen() {
875 return currentCase != null;
876 }
877
885 public static Case getCurrentCase() {
886 try {
887 return getCurrentCaseThrows();
888 } catch (NoCurrentCaseException ex) {
889 /*
890 * Throw a runtime exception, since this is a programming error.
891 */
892 throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
893 }
894 }
895
911 /*
912 * TODO (JIRA-3825): Introduce a reference counting scheme for this get
913 * case method.
914 */
915 Case openCase = currentCase;
916 if (openCase == null) {
917 throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
918 } else {
919 return openCase;
920 }
921 }
922
931 @Messages({
932 "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
933 "Case.progressIndicatorTitle.closingCase=Closing Case"
934 })
935 public static void closeCurrentCase() throws CaseActionException {
936 synchronized (caseActionSerializationLock) {
937 if (null == currentCase) {
938 return;
939 }
940 Case closedCase = currentCase;
941 try {
942 eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
943 logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
944 closedCase.doCloseCaseAction();
945 logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
946 } catch (CaseActionException ex) {
947 logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
948 throw ex;
949 } finally {
950 currentCase = null;
953 }
954 }
955 }
956 }
957
966 public static void deleteCurrentCase() throws CaseActionException {
967 synchronized (caseActionSerializationLock) {
968 if (null == currentCase) {
969 return;
970 }
971 CaseMetadata metadata = currentCase.getMetadata();
974 }
975 }
976
987 @Messages({
988 "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
989 })
990 static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
991 synchronized (caseActionSerializationLock) {
992 if (null == currentCase) {
993 return;
994 }
995
996 /*
997 * Close the current case to release the shared case lock.
998 */
999 CaseMetadata caseMetadata = currentCase.getMetadata();
1001
1002 /*
1003 * Re-open the case with an exclusive case lock, delete the data
1004 * source, and close the case again, releasing the exclusive case
1005 * lock.
1006 */
1007 Case theCase = new Case(caseMetadata);
1008 theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
1009
1010 /*
1011 * Re-open the case with a shared case lock.
1012 */
1013 openAsCurrentCase(new Case(caseMetadata), false);
1014 }
1015 }
1016
1028 @Messages({
1029 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
1030 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
1031 "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
1032 })
1034 synchronized (caseActionSerializationLock) {
1035 if (null != currentCase) {
1036 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
1037 }
1038 }
1039
1040 ProgressIndicator progressIndicator;
1042 progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
1043 } else {
1044 progressIndicator = new LoggingProgressIndicator();
1045 }
1046 progressIndicator.start(Bundle.Case_progressMessage_preparing());
1047 try {
1048 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1049 deleteSingleUserCase(metadata, progressIndicator);
1050 } else {
1051 try {
1052 deleteMultiUserCase(metadata, progressIndicator);
1053 } catch (InterruptedException ex) {
1054 /*
1055 * Note that task cancellation is not currently supported
1056 * for this code path, so this catch block is not expected
1057 * to be executed.
1058 */
1059 throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.getCaseDisplayName()), ex);
1060 }
1061 }
1062 } finally {
1063 progressIndicator.finish();
1064 }
1065 }
1066
1077 @Messages({
1078 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1079 "Case.progressIndicatorTitle.openingCase=Opening Case",
1080 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
1081 })
1082 private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
1083 synchronized (caseActionSerializationLock) {
1084 if (null != currentCase) {
1085 try {
1087 } catch (CaseActionException ex) {
1088 /*
1089 * Notify the user and continue (the error has already been
1090 * logged in closeCurrentCase.
1091 */
1092 MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
1093 }
1094 }
1095 try {
1096 logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
1097 String progressIndicatorTitle;
1099 if (isNewCase) {
1100 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
1101 openCaseAction = newCurrentCase::create;
1102 } else {
1103 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
1104 openCaseAction = newCurrentCase::open;
1105 }
1106 newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null);
1107 currentCase = newCurrentCase;
1108 logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
1110 updateGUIForCaseOpened(newCurrentCase);
1111 }
1112 eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
1113 } catch (CaseActionCancelledException ex) {
1114 logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
1115 throw ex;
1116 } catch (CaseActionException ex) {
1117 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
1118 throw ex;
1119 }
1120 }
1121 }
1122
1131 private static String displayNameToUniqueName(String caseDisplayName) {
1132 /*
1133 * Replace all non-ASCII characters.
1134 */
1135 String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
1136
1137 /*
1138 * Replace all control characters.
1139 */
1140 uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
1141
1142 /*
1143 * Replace /, \, :, ?, space, ' ".
1144 */
1145 uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
1146
1147 /*
1148 * Make it all lowercase.
1149 */
1150 uniqueCaseName = uniqueCaseName.toLowerCase();
1151
1152 /*
1153 * Add a time stamp for uniqueness.
1154 */
1155 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
1156 Date date = new Date();
1157 uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
1158
1159 return uniqueCaseName;
1160 }
1161
1171 public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
1172 /*
1173 * Check the case directory path and permissions. The case directory may
1174 * already exist.
1175 */
1176 File caseDir = new File(caseDirPath);
1177 if (caseDir.exists()) {
1178 if (caseDir.isFile()) {
1179 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
1180 } else if (!caseDir.canRead() || !caseDir.canWrite()) {
1181 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
1182 }
1183 }
1184
1185 /*
1186 * Create the case directory, if it does not already exist.
1187 */
1188 if (!caseDir.mkdirs()) {
1189 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
1190 }
1191
1192 /*
1193 * Create the subdirectories of the case directory, if they do not
1194 * already exist. Note that multi-user cases get an extra layer of
1195 * subdirectories, one subdirectory per application host machine.
1196 */
1197 String hostPathComponent = "";
1198 if (caseType == CaseType.MULTI_USER_CASE) {
1199 hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
1200 }
1201
1202 Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
1203 if (!exportDir.toFile().mkdirs()) {
1204 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
1205 }
1206
1207 Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
1208 if (!logsDir.toFile().mkdirs()) {
1209 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
1210 }
1211
1212 Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
1213 if (!cacheDir.toFile().mkdirs()) {
1214 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
1215 }
1216
1217 Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
1218 if (!moduleOutputDir.toFile().mkdirs()) {
1219 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
1220 }
1221
1222 Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
1223 if (!reportsDir.toFile().mkdirs()) {
1224 throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
1225 }
1226 }
1227
1235 static Map<Long, String> getImagePaths(SleuthkitCase db) {
1236 Map<Long, String> imgPaths = new HashMap<>();
1237 try {
1238 Map<Long, List<String>> imgPathsList = db.getImagePaths();
1239 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
1240 if (entry.getValue().size() > 0) {
1241 imgPaths.put(entry.getKey(), entry.getValue().get(0));
1242 }
1243 }
1244 } catch (TskCoreException ex) {
1245 logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
1246 }
1247 return imgPaths;
1248 }
1249
1260 @Messages({
1261 "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
1262 })
1264 try {
1265 Path caseDirPath = Paths.get(caseDir);
1266 String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath);
1268 return lock;
1269 } catch (InterruptedException ex) {
1270 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
1271 } catch (CoordinationServiceException ex) {
1272 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1273 }
1274 }
1275
1276 private static String getNameForTitle() {
1277 //Method should become unnecessary once technical debt story 3334 is done.
1278 if (UserPreferences.getAppName().equals(Version.getName())) {
1279 //Available version number is version number for this application
1280 return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
1281 } else {
1282 return UserPreferences.getAppName();
1283 }
1284 }
1285
1289 private static void updateGUIForCaseOpened(Case newCurrentCase) {
1290 /*
1291 * If the case database was upgraded for a new schema and a backup
1292 * database was created, notify the user.
1293 */
1294 SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
1295 String backupDbPath = caseDb.getBackupDatabasePath();
1296 if (null != backupDbPath) {
1297 JOptionPane.showMessageDialog(
1298 mainFrame,
1299 NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
1300 NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1301 JOptionPane.INFORMATION_MESSAGE);
1302 }
1303
1304 /*
1305 * Look for the files for the data sources listed in the case database
1306 * and give the user the opportunity to locate any that are missing.
1307 */
1308 Map<Long, String> imgPaths = getImagePaths(caseDb);
1309 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1310 long obj_id = entry.getKey();
1311 String path = entry.getValue();
1312 boolean fileExists = (new File(path).exists()|| DriveUtils.driveExists(path));
1313 if (!fileExists) {
1314 try {
1315 DataSource ds = newCurrentCase.getSleuthkitCase().getDataSource(obj_id);
1316 String hostName = StringUtils.defaultString(ds.getHost() == null ? "" : ds.getHost().getName());
1317 // Using invokeAndWait means that the dialog will
1318 // open on the EDT but this thread will wait for an
1319 // answer. Using invokeLater would cause this loop to
1320 // end before all of the dialogs appeared.
1321 SwingUtilities.invokeAndWait(new Runnable() {
1322 @Override
1323 public void run() {
1324 int response = JOptionPane.showConfirmDialog(
1325 mainFrame,
1326 NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", hostName, path),
1327 NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1328 JOptionPane.YES_NO_OPTION);
1329 if (response == JOptionPane.YES_OPTION) {
1330 MissingImageDialog.makeDialog(obj_id, caseDb);
1331 } else {
1332 logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1333
1334 }
1335 }
1336
1337 });
1338 } catch (InterruptedException | InvocationTargetException | TskCoreException | TskDataException ex) {
1339 logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS
1340 }
1341 }
1342 }
1343
1344 /*
1345 * Enable the case-specific actions.
1346 */
1347 CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
1348 CallableSystemAction.get(OpenHostsAction.class).setEnabled(true);
1349 CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1350 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1351 CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
1352 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase());
1353 CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1354 CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1355 CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1356 CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1357 CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true);
1358
1359 /*
1360 * Add the case to the recent cases tracker that supplies a list of
1361 * recent cases to the recent cases menu item and the open/create case
1362 * dialog.
1363 */
1364 RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1365 final boolean hasData = newCurrentCase.hasData();
1366
1367 SwingUtilities.invokeLater(() -> {
1368 /*
1369 * Open the top components (windows within the main application
1370 * window).
1371 *
1372 * Note: If the core windows are not opened here, they will be
1373 * opened via the DirectoryTreeTopComponent 'propertyChange()'
1374 * method on a DATA_SOURCE_ADDED event.
1375 */
1376 mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1377 if (hasData) {
1379 } else {
1380 //ensure that the DirectoryTreeTopComponent is open so that it's listener can open the core windows including making it visible.
1382 }
1383 mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1384
1385 /*
1386 * Reset the main window title to:
1387 *
1388 * [curent case display name] - [application name].
1389 */
1390 mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1391 });
1392 }
1393
1394 /*
1395 * Update the GUI to to reflect the lack of a current case.
1396 */
1397 private static void updateGUIForCaseClosed() {
1399 SwingUtilities.invokeLater(() -> {
1400 /*
1401 * Close the top components (windows within the main application
1402 * window).
1403 */
1405
1406 /*
1407 * Disable the case-specific menu items.
1408 */
1409 CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1410 CallableSystemAction.get(OpenHostsAction.class).setEnabled(false);
1411 CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1412 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1413 CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
1414 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1415 CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1416 CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1417 CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1418 CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1419 CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(false);
1420
1421 /*
1422 * Clear the notifications in the notfier component in the lower
1423 * right hand corner of the main application window.
1424 */
1426
1427 /*
1428 * Reset the main window title to be just the application name,
1429 * instead of [curent case display name] - [application name].
1430 */
1431 mainFrame.setTitle(getNameForTitle());
1432 });
1433 }
1434 }
1435
1441 public SleuthkitCase getSleuthkitCase() {
1442 return this.caseDb;
1443 }
1444
1451 return caseServices;
1452 }
1453
1460 return metadata.getCaseType();
1461 }
1462
1468 public String getCreatedDate() {
1469 return metadata.getCreatedDate();
1470 }
1471
1477 public String getName() {
1478 return metadata.getCaseName();
1479 }
1480
1486 public String getDisplayName() {
1487 return metadata.getCaseDisplayName();
1488 }
1489
1495 public String getNumber() {
1496 return metadata.getCaseNumber();
1497 }
1498
1504 public String getExaminer() {
1505 return metadata.getExaminer();
1506 }
1507
1513 public String getExaminerPhone() {
1514 return metadata.getExaminerPhone();
1515 }
1516
1522 public String getExaminerEmail() {
1523 return metadata.getExaminerEmail();
1524 }
1525
1531 public String getCaseNotes() {
1532 return metadata.getCaseNotes();
1533 }
1534
1540 public String getCaseDirectory() {
1541 return metadata.getCaseDirectory();
1542 }
1543
1552 public String getOutputDirectory() {
1553 String caseDirectory = getCaseDirectory();
1554 Path hostPath;
1555 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1556 hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1557 } else {
1558 hostPath = Paths.get(caseDirectory);
1559 }
1560 if (!hostPath.toFile().exists()) {
1561 hostPath.toFile().mkdirs();
1562 }
1563 return hostPath.toString();
1564 }
1565
1569 private Path getBaseSystemTempPath() {
1570 return Paths.get(System.getProperty("java.io.tmpdir"), APP_NAME, getName());
1571 }
1572
1579 public String getTempDirectory() {
1580 // NOTE: UserPreferences may also be affected by changes in this method.
1581 // See JIRA-7505 for more information.
1582 Path basePath = null;
1583 // get base temp path for the case based on user preference
1585 case CUSTOM:
1586 String customDirectory = UserMachinePreferences.getCustomTempDirectory();
1587 basePath = (StringUtils.isBlank(customDirectory))
1588 ? null
1589 : Paths.get(customDirectory, APP_NAME, getName());
1590 break;
1591 case CASE:
1592 basePath = Paths.get(getCaseDirectory());
1593 break;
1594 case SYSTEM:
1595 default:
1596 // at this level, if the case directory is specified for a temp
1597 // directory, return the system temp directory instead.
1598 basePath = getBaseSystemTempPath();
1599 break;
1600 }
1601
1602 basePath = basePath == null ? getBaseSystemTempPath() : basePath;
1603
1604 // get sub directories based on multi user vs. single user
1605 Path caseRelPath = (CaseType.MULTI_USER_CASE.equals(getCaseType()))
1607 : Paths.get(TEMP_FOLDER);
1608
1609 File caseTempDir = basePath
1610 .resolve(caseRelPath)
1611 .toFile();
1612
1613 // ensure directory exists
1614 if (!caseTempDir.exists()) {
1615 caseTempDir.mkdirs();
1616 }
1617
1618 return caseTempDir.getAbsolutePath();
1619 }
1620
1627 public String getCacheDirectory() {
1629 }
1630
1637 public String getExportDirectory() {
1639 }
1640
1647 public String getLogDirectoryPath() {
1649 }
1650
1657 public String getReportDirectory() {
1659 }
1660
1667 public String getConfigDirectory() {
1669 }
1670
1677 public String getModuleDirectory() {
1679 }
1680
1689 Path path = Paths.get(getModuleDirectory());
1691 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1692 } else {
1693 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1694 }
1695 }
1696
1706 public List<Content> getDataSources() throws TskCoreException {
1707 return caseDb.getRootObjects();
1708 }
1709
1715 public Set<TimeZone> getTimeZones() {
1716 Set<TimeZone> timezones = new HashSet<>();
1717 String query = "SELECT time_zone FROM data_source_info";
1718 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
1719 ResultSet timeZoneSet = dbQuery.getResultSet();
1720 while (timeZoneSet.next()) {
1721 String timeZone = timeZoneSet.getString("time_zone");
1722 if (timeZone != null && !timeZone.isEmpty()) {
1723 timezones.add(TimeZone.getTimeZone(timeZone));
1724 }
1725 }
1726 } catch (TskCoreException | SQLException ex) {
1727 logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1728 }
1729 return timezones;
1730 }
1731
1738 public String getTextIndexName() {
1739 return getMetadata().getTextIndexName();
1740 }
1741
1747 public boolean hasData() {
1748 return hasData;
1749 }
1750
1756 public boolean hasDataSource() {
1757 return hasDataSource;
1758 }
1759
1770 public void notifyAddingDataSource(UUID eventId) {
1771 hasDataSource = true;
1772 hasData = true;
1773 eventPublisher.publish(new AddingDataSourceEvent(eventId));
1774 }
1775
1786 public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1787 eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1788 }
1789
1801 public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1802 eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1803 }
1804
1814 public void notifyDataSourceNameChanged(Content dataSource, String newName) {
1815 eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
1816 }
1817
1825 public void notifyContentTagAdded(ContentTag newTag) {
1826 notifyContentTagAdded(newTag, null);
1827 }
1828
1838 public void notifyContentTagAdded(ContentTag newTag, List<ContentTag> deletedTagList) {
1839 eventPublisher.publish(new ContentTagAddedEvent(newTag, deletedTagList));
1840 }
1841
1849 public void notifyContentTagDeleted(ContentTag deletedTag) {
1850 eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1851 }
1852
1860 public void notifyTagDefinitionChanged(String changedTagName) {
1861 //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1862 eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1863 }
1864
1875 public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1876 try {
1877 eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1878 } catch (NoCurrentCaseException ex) {
1879 logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1880 }
1881 }
1882
1890 public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1892 }
1893
1903 public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List<BlackboardArtifactTag> removedTagList) {
1904 eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag, removedTagList));
1905 }
1906
1914 public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1915 eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1916 }
1917
1929 public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1930 addReport(localPath, srcModuleName, reportName, null);
1931 }
1932
1947 public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1948 String normalizedLocalPath;
1949 try {
1950 if (localPath.toLowerCase().contains("http:")) {
1951 normalizedLocalPath = localPath;
1952 } else {
1953 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1954 }
1955 } catch (InvalidPathException ex) {
1956 String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1957 throw new TskCoreException(errorMsg, ex);
1958 }
1959 hasData = true;
1960
1961 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1963 return report;
1964 }
1965
1974 public List<Report> getAllReports() throws TskCoreException {
1975 return this.caseDb.getAllReports();
1976 }
1977
1986 public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1987 for (Report report : reports) {
1988 this.caseDb.deleteReport(report);
1989 }
1990
1991 try {
1992 hasData = dbHasData();
1993 } catch (TskCoreException ex) {
1994 logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
1995 }
1996
1997 for (Report report : reports) {
1998 eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1999 }
2000 }
2001
2008 return metadata;
2009 }
2010
2018 @Messages({
2019 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
2020 })
2021 void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
2022 CaseDetails oldCaseDetails = metadata.getCaseDetails();
2023 try {
2024 metadata.setCaseDetails(caseDetails);
2025 } catch (CaseMetadataException ex) {
2026 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
2027 }
2028 if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
2029 try {
2030 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2031 nodeData.setDisplayName(caseDetails.getCaseDisplayName());
2032 CaseNodeData.writeCaseNodeData(nodeData);
2033 } catch (CaseNodeDataException | InterruptedException ex) {
2034 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2035 }
2036 }
2037 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
2038 eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
2039 }
2040 if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
2041 eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
2042 }
2043 if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
2044 eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
2045 }
2046 eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
2047 if (RuntimeProperties.runningWithGUI()) {
2048 SwingUtilities.invokeLater(() -> {
2049 mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
2050 try {
2051 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
2052 } catch (Exception ex) {
2053 logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
2054 }
2055 });
2056 }
2057 }
2058
2071 private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
2072 this(new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails));
2073 }
2074
2080 private Case(CaseMetadata caseMetaData) {
2081 metadata = caseMetaData;
2083 }
2084
2114 @Messages({
2115 "Case.progressIndicatorCancelButton.label=Cancel",
2116 "Case.progressMessage.preparing=Preparing...",
2117 "Case.progressMessage.cancelling=Cancelling...",
2118 "Case.exceptionMessage.cancelled=Cancelled.",
2119 "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
2120 })
2121 private void doOpenCaseAction(String progressIndicatorTitle, CaseAction<ProgressIndicator, Object, Void> caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException {
2122 /*
2123 * Create and start either a GUI progress indicator (with or without a
2124 * cancel button) or a logging progress indicator.
2125 */
2126 CancelButtonListener cancelButtonListener = null;
2127 ProgressIndicator progressIndicator;
2129 if (allowCancellation) {
2130 cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
2131 progressIndicator = new ModalDialogProgressIndicator(
2132 mainFrame,
2133 progressIndicatorTitle,
2134 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2135 Bundle.Case_progressIndicatorCancelButton_label(),
2136 cancelButtonListener);
2137 } else {
2138 progressIndicator = new ModalDialogProgressIndicator(
2139 mainFrame,
2140 progressIndicatorTitle);
2141 }
2142 } else {
2143 progressIndicator = new LoggingProgressIndicator();
2144 }
2145 progressIndicator.start(Bundle.Case_progressMessage_preparing());
2146
2147 /*
2148 * Do the case action in the single thread in the case action executor.
2149 * If the case is a multi-user case, a case lock is acquired and held
2150 * until explictly released and an exclusive case resources lock is
2151 * aquired and held for the duration of the action.
2152 */
2153 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
2154 caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory);
2155 Future<Void> future = caseActionExecutor.submit(() -> {
2156 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2157 caseAction.execute(progressIndicator, additionalParams);
2158 } else {
2159 acquireCaseLock(caseLockType);
2160 try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
2161 if (null == resourcesLock) {
2162 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2163 }
2164 caseAction.execute(progressIndicator, additionalParams);
2165 } catch (CaseActionException ex) {
2167 throw ex;
2168 }
2169 }
2170 return null;
2171 });
2172 if (null != cancelButtonListener) {
2173 cancelButtonListener.setCaseActionFuture(future);
2174 }
2175
2176 /*
2177 * Wait for the case action task to finish.
2178 */
2179 try {
2180 future.get();
2181 } catch (InterruptedException discarded) {
2182 /*
2183 * The thread this method is running in has been interrupted.
2184 */
2185 if (null != cancelButtonListener) {
2186 cancelButtonListener.actionPerformed(null);
2187 } else {
2188 future.cancel(true);
2189 }
2191 } catch (CancellationException discarded) {
2192 /*
2193 * The case action has been cancelled.
2194 */
2196 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2197 } catch (ExecutionException ex) {
2198 /*
2199 * The case action has thrown an exception.
2200 */
2202 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
2203 } finally {
2204 progressIndicator.finish();
2205 }
2206 }
2207
2223 private Void create(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2224 assert (additionalParams == null);
2225 try {
2227 createCaseDirectoryIfDoesNotExist(progressIndicator);
2229 switchLoggingToCaseLogsDirectory(progressIndicator);
2231 saveCaseMetadataToFile(progressIndicator);
2233 createCaseNodeData(progressIndicator);
2236 createCaseDatabase(progressIndicator);
2238 openCaseLevelServices(progressIndicator);
2240 openAppServiceCaseResources(progressIndicator, true);
2242 openCommunicationChannels(progressIndicator);
2243 return null;
2244
2245 } catch (CaseActionException ex) {
2246 /*
2247 * Cancellation or failure. The sleep is a little hack to clear the
2248 * interrupted flag for this thread if this is a cancellation
2249 * scenario, so that the clean up can run to completion in the
2250 * current thread.
2251 */
2252 try {
2253 Thread.sleep(1);
2254 } catch (InterruptedException discarded) {
2255 }
2256 close(progressIndicator);
2257 throw ex;
2258 }
2259 }
2260
2275 private Void open(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2276 assert (additionalParams == null);
2277 try {
2279 switchLoggingToCaseLogsDirectory(progressIndicator);
2281 updateCaseNodeData(progressIndicator);
2283 deleteTempfilesFromCaseDirectory(progressIndicator);
2285 openCaseDataBase(progressIndicator);
2287 openCaseLevelServices(progressIndicator);
2289 openAppServiceCaseResources(progressIndicator, false);
2291 openCommunicationChannels(progressIndicator);
2296 return null;
2297
2298 } catch (CaseActionException ex) {
2299 /*
2300 * Cancellation or failure. The sleep is a little hack to clear the
2301 * interrupted flag for this thread if this is a cancellation
2302 * scenario, so that the clean up can run to completion in the
2303 * current thread.
2304 */
2305 try {
2306 Thread.sleep(1);
2307 } catch (InterruptedException discarded) {
2308 }
2309 close(progressIndicator);
2310 throw ex;
2311 }
2312 }
2313
2318 @Messages({
2319 "# {0} - paths",
2320 "Case_checkImagePaths_noPaths=The following images had no associated paths: {0}",
2321 "Case_checkImagePaths_exceptionOccurred=An exception occurred while checking if image paths are present"
2322 })
2323 private void checkImagePaths() throws CaseActionException {
2324 // if there is a content provider, images don't necessarily need paths
2325 if (StringUtils.isNotBlank(this.metadata.getContentProviderName())) {
2326 return;
2327 }
2328
2329 // identify images without paths
2330 try {
2331 List<Image> noPathImages = new ArrayList<>();
2332 List<Image> images = this.caseDb.getImages();
2333 for (Image img: images) {
2334 if (ArrayUtils.isEmpty(img.getPaths())) {
2335 noPathImages.add(img);
2336 }
2337 }
2338
2339 if (!noPathImages.isEmpty()) {
2340 String imageListStr = noPathImages.stream().map(Image::getName).collect(Collectors.joining(", "));
2341 throw new CaseActionException(Bundle.Case_checkImagePaths_noPaths(imageListStr));
2342 }
2343 } catch (TskCoreException ex) {
2344 throw new CaseActionException(Bundle.Case_checkImagePaths_exceptionOccurred(), ex);
2345 }
2346 }
2347
2357 @Messages({
2358 "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...",
2359 "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..."
2360 })
2364 }
2365
2368 }
2369
2374 private static class BackgroundOpenFileSystemsTask implements Runnable {
2375
2376 private final SleuthkitCase tskCase;
2377 private final String caseName;
2378 private final long MAX_IMAGE_THRESHOLD = 100;
2380
2389 BackgroundOpenFileSystemsTask(SleuthkitCase tskCase, ProgressIndicator progressIndicator) {
2390 this.tskCase = tskCase;
2391 this.progressIndicator = progressIndicator;
2392 caseName = (this.tskCase != null) ? this.tskCase.getDatabaseName() : "";
2393 }
2394
2402 private void checkIfCancelled() throws InterruptedException {
2403 if (Thread.interrupted()) {
2404 throw new InterruptedException();
2405 }
2406 }
2407
2413 private List<Image> getImages() {
2414 progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName));
2415 try {
2416 return this.tskCase.getImages();
2417 } catch (TskCoreException ex) {
2418 logger.log(
2419 Level.SEVERE,
2420 String.format("Could not obtain images while opening case: %s.", caseName),
2421 ex);
2422
2423 return null;
2424 }
2425 }
2426
2436 private void openFileSystems(List<Image> images) throws TskCoreException, InterruptedException {
2437 byte[] tempBuff = new byte[512];
2438
2439 for (Image image : images) {
2440 String imageStr = image.getName();
2441
2442 progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr));
2443
2444 Collection<FileSystem> fileSystems = this.tskCase.getImageFileSystems(image);
2446 for (FileSystem fileSystem : fileSystems) {
2447 fileSystem.read(tempBuff, 0, 512);
2449 }
2450
2451 }
2452 }
2453
2454 @Override
2455 public void run() {
2456 try {
2458 List<Image> images = getImages();
2459 if (images == null) {
2460 return;
2461 }
2462
2463 if (images.size() > MAX_IMAGE_THRESHOLD) {
2464 // If we have a large number of images, don't try to preload anything
2465 logger.log(
2466 Level.INFO,
2467 String.format("Skipping background load of file systems due to large number of images in case (%d)", images.size()));
2468 return;
2469 }
2470
2472 openFileSystems(images);
2473 } catch (InterruptedException ex) {
2474 logger.log(
2475 Level.INFO,
2476 String.format("Background operation opening all file systems in %s has been cancelled.", caseName));
2477 } catch (Exception ex) {
2478 // Exception firewall
2479 logger.log(Level.WARNING, "Error while opening file systems in background", ex);
2480 }
2481 }
2482
2483 }
2484
2499 @Messages({
2500 "Case.progressMessage.deletingDataSource=Removing the data source from the case...",
2501 "Case.exceptionMessage.dataSourceNotFound=The data source was not found.",
2502 "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.",
2503 "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",})
2504 Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2505 assert (additionalParams instanceof Long);
2506 open(progressIndicator, null);
2507 try {
2508 progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource());
2509 Long dataSourceObjectID = (Long) additionalParams;
2510 try {
2511 DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID);
2512 if (dataSource == null) {
2513 throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound());
2514 }
2515 SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID);
2516 } catch (TskDataException | TskCoreException ex) {
2517 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex);
2518 }
2519 try {
2520 this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID);
2521 } catch (KeywordSearchServiceException ex) {
2522 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex);
2523 }
2524 eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID));
2525 return null;
2526 } finally {
2527 close(progressIndicator);
2529 }
2530 }
2531
2542 public SleuthkitCase createPortableCase(String caseName, File portableCaseFolder) throws TskCoreException {
2543
2544 if (portableCaseFolder.exists()) {
2545 throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
2546 }
2547 if (!portableCaseFolder.mkdirs()) {
2548 throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
2549 }
2550
2551 CaseDetails details = new CaseDetails(caseName, getNumber(), getExaminer(),
2553 try {
2554 CaseMetadata portableCaseMetadata = new CaseMetadata(Case.CaseType.SINGLE_USER_CASE, portableCaseFolder.toString(),
2555 caseName, details, metadata);
2556 portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2557 } catch (CaseMetadataException ex) {
2558 throw new TskCoreException("Error creating case metadata", ex);
2559 }
2560
2561 // Create the Sleuthkit case
2562 SleuthkitCase portableSleuthkitCase;
2563 String dbFilePath = Paths.get(portableCaseFolder.toString(), SINGLE_USER_CASE_DB_NAME).toString();
2564 portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
2565
2566 return portableSleuthkitCase;
2567 }
2568
2579 if (Thread.currentThread().isInterrupted()) {
2580 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2581 }
2582 }
2583
2594 @Messages({
2595 "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
2596 })
2598 /*
2599 * TODO (JIRA-2180): Always create the case directory as part of the
2600 * case creation process.
2601 */
2602 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2603 if (new File(metadata.getCaseDirectory()).exists() == false) {
2604 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2605 Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
2606 }
2607 }
2608
2615 @Messages({
2616 "Case.progressMessage.switchingLogDirectory=Switching log directory..."
2617 })
2618 private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
2619 progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2621 }
2622
2634 @Messages({
2635 "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2636 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2637 })
2638 private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
2639 progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
2640 try {
2641 this.metadata.writeToFile();
2642 } catch (CaseMetadataException ex) {
2643 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2644 }
2645 }
2646
2658 @Messages({
2659 "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2660 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2661 })
2662 private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2664 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2665 try {
2667 } catch (CaseNodeDataException | InterruptedException ex) {
2668 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2669 }
2670 }
2671 }
2672
2684 @Messages({
2685 "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2686 "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2687 })
2688 private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2690 progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2691 try {
2692 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2693 nodeData.setLastAccessDate(new Date());
2695 } catch (CaseNodeDataException | InterruptedException ex) {
2696 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2697 }
2698 }
2699 }
2700
2706 @Messages({
2707 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2708 })
2709 private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
2710 /*
2711 * Clear the temp subdirectory of the case directory.
2712 */
2713 progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2714 FileUtil.deleteDir(new File(this.getTempDirectory()));
2715 }
2716
2728 @Messages({
2729 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2730 "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2731 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2732 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2733 })
2734 private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
2735 progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2736 try {
2737 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2738 /*
2739 * For single-user cases, the case database is a SQLite database
2740 * with a standard name, physically located in the case
2741 * directory.
2742 */
2743 caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString(), (ContentStreamProvider) null, APP_NAME);
2744 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2745 } else {
2746 /*
2747 * For multi-user cases, the case database is a PostgreSQL
2748 * database with a name derived from the case display name,
2749 * physically located on the PostgreSQL database server.
2750 */
2751 caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2752 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2753 }
2754 } catch (TskCoreException ex) {
2756 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2757 } catch (UserPreferencesException ex) {
2758 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2759 } catch (CaseMetadataException ex) {
2760 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2761 }
2762 }
2763
2775 @Messages({
2776 "Case.progressMessage.openingCaseDatabase=Opening case database...",
2777 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2778 "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2779 "Case.exceptionMessage.contentProviderCouldNotBeFound=Content provider was specified for the case but could not be loaded.",
2780 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2781 })
2782 private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
2783 progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2784 try {
2785 String databaseName = metadata.getCaseDatabaseName();
2786
2787 ContentStreamProvider contentProvider = loadContentProvider(metadata.getContentProviderName());
2788 if (StringUtils.isNotBlank(metadata.getContentProviderName()) && contentProvider == null) {
2789 if (metadata.getContentProviderName().trim().toUpperCase().startsWith(CT_PROVIDER_PREFIX.toUpperCase())) {
2790 new CTIntegrationMissingDialog(WindowManager.getDefault().getMainWindow(), true).showDialog(null);
2791 }
2792 throw new CaseActionException(Bundle.Case_exceptionMessage_contentProviderCouldNotBeFound());
2793 }
2794
2795 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2796 caseDb = SleuthkitCase.openCase(metadata.getCaseDatabasePath(), contentProvider, APP_NAME);
2798 caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory(), contentProvider);
2799 } else {
2800 throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2801 }
2803 } catch (TskUnsupportedSchemaVersionException ex) {
2804 throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2805 } catch (UserPreferencesException ex) {
2806 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2807 } catch (TskCoreException ex) {
2809 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2810 }
2811 }
2812
2813
2819 @Messages({
2820 "# {0} - appplicationName",
2821 "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.",
2822 "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp=another application"
2823 })
2825 ConcurrentDbAccessException concurrentEx = null;
2826 Throwable curEx = ex;
2827 // max depth search for a concurrent db access exception will be 10
2828 for (int i = 0; i < 10; i++) {
2829 if (curEx == null) {
2830 break;
2831 } else if (curEx instanceof ConcurrentDbAccessException foundEx) {
2832 concurrentEx = foundEx;
2833 break;
2834 } else {
2835 curEx = curEx.getCause();
2836 }
2837 }
2838
2839 if (concurrentEx != null) {
2840 throw new CaseActionException(Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException(
2841 StringUtils.defaultIfBlank(concurrentEx.getConflictingApplicationName(),
2842 Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp())
2843 ), concurrentEx);
2844 }
2845 }
2846
2847
2857 private static ContentStreamProvider loadContentProvider(String providerName) {
2858 Collection<? extends AutopsyContentProvider> customContentProviders = Lookup.getDefault().lookupAll(AutopsyContentProvider.class);
2859 if (customContentProviders != null) {
2860 for (AutopsyContentProvider customProvider : customContentProviders) {
2861 // ensure the provider matches the name
2862 if (customProvider == null || !StringUtils.equalsIgnoreCase(providerName, customProvider.getName())) {
2863 continue;
2864 }
2865
2866 ContentStreamProvider contentProvider = customProvider.load();
2867 if (contentProvider != null) {
2868 return contentProvider;
2869 }
2870 }
2871 }
2872
2873 return null;
2874 }
2875
2876
2883 @Messages({
2884 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2885 private void openCaseLevelServices(ProgressIndicator progressIndicator) {
2886 progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2887 this.caseServices = new Services(caseDb);
2888 /*
2889 * RC Note: JM put this initialization here. I'm not sure why. However,
2890 * my attempt to put it in the openCaseDatabase method seems to lead to
2891 * intermittent unchecked exceptions concerning a missing subscriber.
2892 */
2893 caseDb.registerForEvents(sleuthkitEventListener);
2894 }
2895
2908 @NbBundle.Messages({
2909 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2910 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2911 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2912 "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2913 })
2914 private void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase) throws CaseActionException {
2915 /*
2916 * Each service gets its own independently cancellable/interruptible
2917 * task, running in a named thread managed by an executor service, with
2918 * its own progress indicator. This allows for cancellation of the
2919 * opening of case resources for individual services. It also makes it
2920 * possible to ensure that each service task completes before the next
2921 * one starts by awaiting termination of the executor service.
2922 */
2923 progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2924
2925 for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
2926 )) {
2927 /*
2928 * Create a progress indicator for the task and start the task. If
2929 * running with a GUI, the progress indicator will be a dialog box
2930 * with a Cancel button.
2931 */
2932 CancelButtonListener cancelButtonListener = null;
2933 ProgressIndicator appServiceProgressIndicator;
2935 cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2936 appServiceProgressIndicator = new ModalDialogProgressIndicator(
2937 mainFrame,
2938 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2939 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2940 Bundle.Case_progressIndicatorCancelButton_label(),
2941 cancelButtonListener);
2942 } else {
2943 appServiceProgressIndicator = new LoggingProgressIndicator();
2944 }
2945 appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
2946 AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator, isNewCase);
2947 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2948 threadNameSuffix = threadNameSuffix.toLowerCase();
2949 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2950 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2951 Future<Void> future = executor.submit(() -> {
2952 service.openCaseResources(context);
2953 return null;
2954 });
2955 if (null != cancelButtonListener) {
2956 cancelButtonListener.setCaseContext(context);
2957 cancelButtonListener.setCaseActionFuture(future);
2958 }
2959
2960 /*
2961 * Wait for the task to either be completed or
2962 * cancelled/interrupted, or for the opening of the case to be
2963 * cancelled.
2964 */
2965 try {
2966 future.get();
2967 } catch (InterruptedException discarded) {
2968 /*
2969 * The parent create/open case task has been cancelled.
2970 */
2971 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()));
2972 future.cancel(true);
2973 } catch (CancellationException discarded) {
2974 /*
2975 * The opening of case resources by the application service has
2976 * been cancelled, so the executor service has thrown. Note that
2977 * there is no guarantee the task itself has responded to the
2978 * cancellation request yet.
2979 */
2980 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()));
2981 } catch (ExecutionException ex) {
2982 /*
2983 * An exception was thrown while executing the task. The
2984 * case-specific application service resources are not
2985 * essential. Log an error and notify the user if running the
2986 * desktop GUI, but do not throw.
2987 */
2988 Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2990 SwingUtilities.invokeLater(() -> {
2991 MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2992 });
2993 }
2994 } finally {
2995 /*
2996 * Shut down the executor service and wait for it to finish.
2997 * This ensures that the task has finished. Without this, it
2998 * would be possible to start the next task before the current
2999 * task responded to a cancellation request.
3000 */
3002 appServiceProgressIndicator.finish();
3003 }
3005 }
3006 }
3007
3019 @Messages({
3020 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
3021 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
3022 "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
3023 })
3024 private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
3025 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
3026 progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
3027 try {
3028 eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
3030 collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
3031 } catch (AutopsyEventException ex) {
3032 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
3033 } catch (CollaborationMonitor.CollaborationMonitorException ex) {
3034 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
3035 }
3036 }
3037 }
3038
3054 /*
3055 * Set up either a GUI progress indicator without a Cancel button or a
3056 * logging progress indicator.
3057 */
3058 ProgressIndicator progressIndicator;
3060 progressIndicator = new ModalDialogProgressIndicator(
3061 mainFrame,
3062 Bundle.Case_progressIndicatorTitle_closingCase());
3063 } else {
3064 progressIndicator = new LoggingProgressIndicator();
3065 }
3066 progressIndicator.start(Bundle.Case_progressMessage_preparing());
3067
3068 /*
3069 * Closing a case is always done in the same non-UI thread that
3070 * opened/created the case. If the case is a multi-user case, this
3071 * ensures that case lock that is held as long as the case is open is
3072 * released in the same thread in which it was acquired, as is required
3073 * by the coordination service.
3074 */
3075 Future<Void> future = caseActionExecutor.submit(() -> {
3076 if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
3077 close(progressIndicator);
3078 } else {
3079 /*
3080 * Acquire an exclusive case resources lock to ensure only one
3081 * node at a time can create/open/upgrade/close the case
3082 * resources.
3083 */
3084 progressIndicator.progress(Bundle.Case_progressMessage_preparing());
3085 try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
3086 if (null == resourcesLock) {
3087 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
3088 }
3089 close(progressIndicator);
3090 } finally {
3091 /*
3092 * Always release the case directory lock that was acquired
3093 * when the case was opened.
3094 */
3096 }
3097 }
3098 return null;
3099 });
3100
3101 try {
3102 future.get();
3103 } catch (InterruptedException | CancellationException unused) {
3104 /*
3105 * The wait has been interrupted by interrupting the thread running
3106 * this method. Not allowing cancellation of case closing, so ignore
3107 * the interrupt. Likewise, cancellation of the case closing task is
3108 * not supported.
3109 */
3110 } catch (ExecutionException ex) {
3111 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
3112 } finally {
3114 progressIndicator.finish();
3115 }
3116 }
3117
3123 @Messages({
3124 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
3125 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
3126 "Case.progressMessage.closingCaseDatabase=Closing case database..."
3127 })
3128 private void close(ProgressIndicator progressIndicator) {
3130
3131 /*
3132 * Stop sending/receiving case events to and from other nodes if this is
3133 * a multi-user case.
3134 */
3135 if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
3136 progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
3137 if (null != collaborationMonitor) {
3138 collaborationMonitor.shutdown();
3139 }
3140 eventPublisher.closeRemoteEventChannel();
3141 }
3142
3143 /*
3144 * Allow all registered application services providers to close
3145 * resources related to the case.
3146 */
3147 progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
3149
3150 /*
3151 * Close the case database.
3152 */
3153 if (null != caseDb) {
3154 progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
3155 caseDb.unregisterForEvents(sleuthkitEventListener);
3156 caseDb.close();
3157 }
3158
3162 deleteTempfilesFromCaseDirectory(progressIndicator);
3163
3164 /*
3165 * Switch the log directory.
3166 */
3167 progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
3169 }
3170
3175 @Messages({
3176 "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
3177 "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
3178 })
3180 /*
3181 * Each service gets its own independently cancellable task, and thus
3182 * its own task progress indicator.
3183 */
3184 for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
3185 )) {
3186 ProgressIndicator progressIndicator;
3188 progressIndicator = new ModalDialogProgressIndicator(
3189 mainFrame,
3190 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
3191 } else {
3192 progressIndicator = new LoggingProgressIndicator();
3193 }
3194 progressIndicator.start(Bundle.Case_progressMessage_preparing());
3195 AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
3196 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
3197 threadNameSuffix = threadNameSuffix.toLowerCase();
3198 TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
3199 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
3200 Future<Void> future = executor.submit(() -> {
3201 service.closeCaseResources(context);
3202 return null;
3203 });
3204 try {
3205 future.get();
3206 } catch (InterruptedException ex) {
3207 Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
3208 } catch (CancellationException ex) {
3209 Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
3210 } catch (ExecutionException ex) {
3211 Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
3213 SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
3214 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
3215 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
3216 }
3217 } finally {
3219 progressIndicator.finish();
3220 }
3221 }
3222 }
3223
3229 @Messages({
3230 "Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.",
3231 "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case."
3232 })
3233 private void acquireCaseLock(CaseLockType lockType) throws CaseActionException {
3234 String caseDir = metadata.getCaseDirectory();
3235 try {
3236 CoordinationService coordinationService = CoordinationService.getInstance();
3237 caseLock = lockType == CaseLockType.SHARED
3238 ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES)
3239 : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES);
3240 if (caseLock == null) {
3241 if (lockType == CaseLockType.SHARED) {
3242 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock());
3243 } else {
3244 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock());
3245 }
3246 }
3247 } catch (InterruptedException | CoordinationServiceException ex) {
3248 if (lockType == CaseLockType.SHARED) {
3249 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex);
3250 } else {
3251 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex);
3252 }
3253 }
3254 }
3255
3259 private void releaseCaseLock() {
3260 if (caseLock != null) {
3261 try {
3262 caseLock.release();
3263 caseLock = null;
3265 logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", getMetadata().getCaseDirectory()), ex);
3266 }
3267 }
3268 }
3269
3276 private String getOrCreateSubdirectory(String subDirectoryName) {
3277 File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
3278 if (!subDirectory.exists()) {
3279 subDirectory.mkdirs();
3280 }
3281 return subDirectory.toString();
3282
3283 }
3284
3296 @Messages({
3297 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
3298 })
3299 private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
3300 boolean errorsOccurred = false;
3301 try {
3302 deleteTextIndex(metadata, progressIndicator);
3303 } catch (KeywordSearchServiceException ex) {
3304 errorsOccurred = true;
3305 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
3306 }
3307
3308 try {
3309 deleteCaseDirectory(metadata, progressIndicator);
3310 } catch (CaseActionException ex) {
3311 errorsOccurred = true;
3312 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
3313 }
3314
3315 deleteFromRecentCases(metadata, progressIndicator);
3316
3317 if (errorsOccurred) {
3318 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
3319 }
3320 }
3321
3341 @Messages({
3342 "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
3343 "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
3344 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
3345 "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
3346 "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
3347 "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
3348 "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
3349 "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
3350 })
3351 private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
3352 progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
3353 CoordinationService coordinationService;
3354 try {
3355 coordinationService = CoordinationService.getInstance();
3356 } catch (CoordinationServiceException ex) {
3357 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
3358 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
3359 }
3360
3361 CaseNodeData caseNodeData;
3362 boolean errorsOccurred = false;
3363 try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
3364 if (dirLock == null) {
3365 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
3366 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
3367 }
3368
3369 progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
3370 try {
3371 caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
3372 } catch (CaseNodeDataException | InterruptedException ex) {
3373 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
3374 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
3375 }
3376
3377 errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator, logger);
3378
3379 progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
3380 try {
3381 String resourcesLockNodePath = CoordinationServiceUtils.getCaseResourcesNodePath(caseNodeData.getDirectory());
3382 coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
3383 } catch (CoordinationServiceException ex) {
3384 if (!isNoNodeException(ex)) {
3385 errorsOccurred = true;
3386 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
3387 }
3388 } catch (InterruptedException ex) {
3389 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
3390 }
3391
3392 } catch (CoordinationServiceException ex) {
3393 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
3394 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
3395 }
3396
3397 if (!errorsOccurred) {
3398 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
3399 try {
3400 String casDirNodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseNodeData.getDirectory());
3401 coordinationService.deleteNode(CategoryNode.CASES, casDirNodePath);
3402 } catch (CoordinationServiceException | InterruptedException ex) {
3403 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
3404 errorsOccurred = true;
3405 }
3406 }
3407
3408 if (errorsOccurred) {
3409 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
3410 }
3411 }
3412
3437 @Beta
3438 public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws InterruptedException {
3439 boolean errorsOccurred = false;
3440 try {
3441 deleteMultiUserCaseDatabase(caseNodeData, metadata, progressIndicator, logger);
3442 deleteMultiUserCaseTextIndex(caseNodeData, metadata, progressIndicator, logger);
3443 deleteMultiUserCaseDirectory(caseNodeData, metadata, progressIndicator, logger);
3444 deleteFromRecentCases(metadata, progressIndicator);
3445 } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
3446 errorsOccurred = true;
3447 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
3448 } catch (KeywordSearchServiceException ex) {
3449 errorsOccurred = true;
3450 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
3451 } catch (CaseActionException ex) {
3452 errorsOccurred = true;
3453 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
3454 }
3455 return errorsOccurred;
3456 }
3457
3478 @Messages({
3479 "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
3480 })
3481 private static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
3483 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
3484 logger.log(Level.INFO, String.format("Deleting case database for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3485 CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
3486 String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
3487 Class.forName("org.postgresql.Driver"); //NON-NLS
3488 try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
3489 String dbExistsQuery = "SELECT 1 from pg_database WHERE datname = '" + metadata.getCaseDatabaseName() + "'"; //NON-NLS
3490 try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
3491 if (queryResult.next()) {
3492 String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
3493 statement.execute(deleteCommand);
3494 }
3495 }
3496 }
3498 }
3499 }
3500
3516 private static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws KeywordSearchServiceException, InterruptedException {
3518 logger.log(Level.INFO, String.format("Deleting text index for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3519 deleteTextIndex(metadata, progressIndicator);
3521 }
3522 }
3523
3533 @Messages({
3534 "Case.progressMessage.deletingTextIndex=Deleting text index..."
3535 })
3537 progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
3538
3539 for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class
3540 )) {
3541 searchService.deleteTextIndex(metadata);
3542 }
3543 }
3544
3559 private static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws CaseActionException, InterruptedException {
3561 logger.log(Level.INFO, String.format("Deleting case directory for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
3562 deleteCaseDirectory(metadata, progressIndicator);
3564 }
3565 }
3566
3576 @Messages({
3577 "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
3578 })
3579 private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
3580 progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
3581 if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
3582 throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory())); //NON-NLS
3583 }
3584 }
3585
3593 @Messages({
3594 "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
3595 })
3596 private static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator) {
3598 progressIndicator.progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
3599 SwingUtilities.invokeLater(() -> {
3600 RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
3601 });
3602 }
3603 }
3604
3615 boolean isNodeNodeEx = false;
3616 Throwable cause = ex.getCause();
3617 if (cause != null) {
3618 String causeMessage = cause.getMessage();
3619 isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
3620 }
3621 return isNodeNodeEx;
3622 }
3623
3635 private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
3636 try {
3637 caseNodeData.setDeletedFlag(flag);
3638 CaseNodeData.writeCaseNodeData(caseNodeData);
3639 } catch (CaseNodeDataException ex) {
3640 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);
3641
3642 }
3643 }
3644
3656 private void updateDataParameters() throws TskCoreException {
3658
3659 if (!hasDataSource) {
3660 hasData = dbHasData();
3661 } else {
3662 hasData = true;
3663 }
3664 }
3665
3673 private boolean dbHasDataSource() throws TskCoreException {
3674 String query = "SELECT count(*) AS count FROM (SELECT * FROM data_source_info LIMIT 1)t";
3675 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
3676 ResultSet resultSet = dbQuery.getResultSet();
3677 if (resultSet.next()) {
3678 return resultSet.getLong("count") > 0;
3679 }
3680 return false;
3681 } catch (SQLException ex) {
3682 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
3683 throw new TskCoreException("Error accessing case databse", ex);
3684 }
3685 }
3686
3695 private boolean dbHasData() throws TskCoreException {
3696 // The LIMIT 1 in the subquery should limit the data returned and
3697 // make the overall query more efficent.
3698 String query = "SELECT SUM(cnt) total FROM "
3699 + "(SELECT COUNT(*) AS cnt FROM "
3700 + "(SELECT * FROM tsk_objects LIMIT 1)t "
3701 + "UNION ALL "
3702 + "SELECT COUNT(*) AS cnt FROM "
3703 + "(SELECT * FROM tsk_hosts LIMIT 1)r) s";
3704 try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
3705 ResultSet resultSet = dbQuery.getResultSet();
3706 if (resultSet.next()) {
3707 return resultSet.getLong("total") > 0;
3708 } else {
3709 return false;
3710 }
3711 } catch (SQLException ex) {
3712 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
3713 throw new TskCoreException("Error accessing case databse", ex);
3714 }
3715 }
3716
3725 private interface CaseAction<T, V, R> {
3726
3737 R execute(T progressIndicator, V additionalParams) throws CaseActionException;
3738 }
3739
3744 private enum CaseLockType {
3746 }
3747
3752 @ThreadSafe
3753 private final static class CancelButtonListener implements ActionListener {
3754
3755 private final String cancellationMessage;
3756 @GuardedBy("this")
3757 private boolean cancelRequested;
3758 @GuardedBy("this")
3760 @GuardedBy("this")
3761 private Future<?> caseActionFuture;
3762
3772 this.cancellationMessage = cancellationMessage;
3773 }
3774
3780 private synchronized void setCaseContext(CaseContext caseContext) {
3781 this.caseContext = caseContext;
3782 /*
3783 * If the cancel button has already been pressed, pass the
3784 * cancellation on to the case context.
3785 */
3786 if (cancelRequested) {
3787 cancel();
3788 }
3789 }
3790
3796 private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
3797 this.caseActionFuture = caseActionFuture;
3798 /*
3799 * If the cancel button has already been pressed, cancel the Future
3800 * of the task.
3801 */
3802 if (cancelRequested) {
3803 cancel();
3804 }
3805 }
3806
3812 @Override
3813 public synchronized void actionPerformed(ActionEvent event) {
3814 cancel();
3815 }
3816
3820 private void cancel() {
3821 /*
3822 * At a minimum, set the cancellation requested flag of this
3823 * listener.
3824 */
3825 this.cancelRequested = true;
3826 if (null != this.caseContext) {
3827 /*
3828 * Set the cancellation request flag and display the
3829 * cancellation message in the progress indicator for the case
3830 * context associated with this listener.
3831 */
3833 ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
3834 if (progressIndicator instanceof ModalDialogProgressIndicator) {
3835 ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
3836 }
3837 }
3838 this.caseContext.requestCancel();
3839 }
3840 if (null != this.caseActionFuture) {
3841 /*
3842 * Cancel the Future of the task associated with this listener.
3843 * Note that the task thread will be interrupted if the task is
3844 * blocked.
3845 */
3846 this.caseActionFuture.cancel(true);
3847 }
3848 }
3849 }
3850
3854 private static class TaskThreadFactory implements ThreadFactory {
3855
3856 private final String threadName;
3857
3859 this.threadName = threadName;
3860 }
3861
3862 @Override
3863 public Thread newThread(Runnable task) {
3864 return new Thread(task, threadName);
3865 }
3866
3867 }
3868
3876 @Deprecated
3877 public static String getAppName() {
3878 return UserPreferences.getAppName();
3879 }
3880
3900 @Deprecated
3901 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
3902 createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
3903 }
3904
3925 @Deprecated
3926 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
3927 createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
3928 }
3929
3941 @Deprecated
3942 public static void open(String caseMetadataFilePath) throws CaseActionException {
3943 openAsCurrentCase(caseMetadataFilePath);
3944 }
3945
3955 @Deprecated
3956 public void closeCase() throws CaseActionException {
3958 }
3959
3965 @Deprecated
3966 public static void invokeStartupDialog() {
3968 }
3969
3983 @Deprecated
3984 public static String convertTimeZone(String timeZoneId) {
3985 return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
3986 }
3987
3997 @Deprecated
3998 public static boolean pathExists(String filePath) {
3999 return new File(filePath).isFile();
4000 }
4001
4010 @Deprecated
4011 public static String getAutopsyVersion() {
4012 return Version.getVersion();
4013 }
4014
4022 @Deprecated
4023 public static boolean existsCurrentCase() {
4024 return isCaseOpen();
4025 }
4026
4036 @Deprecated
4037 public static String getModulesOutputDirRelPath() {
4038 return "ModuleOutput"; //NON-NLS
4039 }
4040
4050 @Deprecated
4051 public static PropertyChangeSupport
4053 return new PropertyChangeSupport(Case.class
4054 );
4055 }
4056
4065 @Deprecated
4067 return getModuleDirectory();
4068 }
4069
4084 @Deprecated
4085 public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
4086 try {
4087 Image newDataSource = caseDb.getImageById(imgId);
4088 notifyDataSourceAdded(newDataSource, UUID.randomUUID());
4089 return newDataSource;
4090 } catch (TskCoreException ex) {
4091 throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
4092 }
4093 }
4094
4102 @Deprecated
4103 public Set<TimeZone> getTimeZone() {
4104 return getTimeZones();
4105 }
4106
4117 @Deprecated
4118 public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
4119 deleteReports(reports);
4120 }
4121
4122}
synchronized void setCaseContext(CaseContext caseContext)
Definition Case.java:3780
synchronized void actionPerformed(ActionEvent event)
Definition Case.java:3813
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition Case.java:3796
void publishOsAccountInstancesAddedEvent(TskEvent.OsAcctInstancesAddedTskEvent event)
Definition Case.java:556
void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event)
Definition Case.java:567
void publicTagNamesDeleted(TskEvent.TagNamesDeletedTskEvent event)
Definition Case.java:654
void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event)
Definition Case.java:541
void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event)
Definition Case.java:634
void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event)
Definition Case.java:530
void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event)
Definition Case.java:629
void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event)
Definition Case.java:535
void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event)
Definition Case.java:590
void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event)
Definition Case.java:618
void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event)
Definition Case.java:607
void publicTagSetsDeleted(TskEvent.TagSetsDeletedTskEvent event)
Definition Case.java:664
void publicTagSetsAdded(TskEvent.TagSetsAddedTskEvent event)
Definition Case.java:659
void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event)
Definition Case.java:579
void publicTagNamesAdded(TskEvent.TagNamesAddedTskEvent event)
Definition Case.java:644
void publicTagNamesUpdated(TskEvent.TagNamesUpdatedTskEvent event)
Definition Case.java:649
void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event)
Definition Case.java:639
void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event)
Definition Case.java:546
static boolean pathExists(String filePath)
Definition Case.java:3998
static void openAsCurrentCase(String caseMetadataFilePath)
Definition Case.java:856
static final String EVENT_CHANNEL_NAME
Definition Case.java:175
void notifyContentTagAdded(ContentTag newTag, List< ContentTag > deletedTagList)
Definition Case.java:1838
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
String getOrCreateSubdirectory(String subDirectoryName)
Definition Case.java:3276
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3351
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition Case.java:1786
static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS
Definition Case.java:171
static String displayNameToUniqueName(String caseDisplayName)
Definition Case.java:1131
Case(CaseMetadata caseMetaData)
Definition Case.java:2080
void doOpenCaseAction(String progressIndicatorTitle, CaseAction< ProgressIndicator, Object, Void > caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams)
Definition Case.java:2121
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3559
Void open(ProgressIndicator progressIndicator, Object additionalParams)
Definition Case.java:2275
void deleteReports(Collection<? extends Report > reports, boolean deleteFromDisk)
Definition Case.java:4118
static final String MODULE_FOLDER
Definition Case.java:181
static volatile Case currentCase
Definition Case.java:193
volatile ExecutorService caseActionExecutor
Definition Case.java:195
static final String CASE_RESOURCES_THREAD_NAME
Definition Case.java:183
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
Definition Case.java:3635
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition Case.java:702
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3516
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition Case.java:2071
static Future<?> backgroundOpenFileSystemsFuture
Definition Case.java:189
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition Case.java:1289
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3596
static final String TEMP_FOLDER
Definition Case.java:173
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition Case.java:2542
void updateCaseNodeData(ProgressIndicator progressIndicator)
Definition Case.java:2688
static final String EXPORT_FOLDER
Definition Case.java:177
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition Case.java:727
static final String SINGLE_USER_CASE_DB_NAME
Definition Case.java:174
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition Case.java:737
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition Case.java:1082
static void open(String caseMetadataFilePath)
Definition Case.java:3942
void createCaseDatabase(ProgressIndicator progressIndicator)
Definition Case.java:2734
void notifyContentTagDeleted(ContentTag deletedTag)
Definition Case.java:1849
static final String CACHE_FOLDER
Definition Case.java:176
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition Case.java:687
void notifyDataSourceNameChanged(Content dataSource, String newName)
Definition Case.java:1814
void notifyAddingDataSource(UUID eventId)
Definition Case.java:1770
void createCaseNodeData(ProgressIndicator progressIndicator)
Definition Case.java:2662
static boolean isValidName(String caseName)
Definition Case.java:771
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition Case.java:1947
static final String NO_NODE_ERROR_MSG_FRAGMENT
Definition Case.java:184
static final Object caseActionSerializationLock
Definition Case.java:188
static final String REPORTS_FOLDER
Definition Case.java:179
static ContentStreamProvider loadContentProvider(String providerName)
Definition Case.java:2857
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
Definition Case.java:2597
void addReport(String localPath, String srcModuleName, String reportName)
Definition Case.java:1929
static String getModulesOutputDirRelPath()
Definition Case.java:4037
static void createCaseDirectory(String caseDirPath, CaseType caseType)
Definition Case.java:1171
final SleuthkitEventListener sleuthkitEventListener
Definition Case.java:198
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition Case.java:747
void openCommunicationChannels(ProgressIndicator progressIndicator)
Definition Case.java:3024
static final AutopsyEventPublisher eventPublisher
Definition Case.java:187
static final ExecutorService openFileSystemsExecutor
Definition Case.java:191
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition Case.java:802
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition Case.java:675
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition Case.java:3901
static String convertTimeZone(String timeZoneId)
Definition Case.java:3984
static final String LOG_FOLDER
Definition Case.java:178
void close(ProgressIndicator progressIndicator)
Definition Case.java:3128
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3536
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List< BlackboardArtifactTag > removedTagList)
Definition Case.java:1903
static final String CONFIG_FOLDER
Definition Case.java:180
void notifyTagDefinitionChanged(String changedTagName)
Definition Case.java:1860
static void deleteCase(CaseMetadata metadata)
Definition Case.java:1033
static CoordinationService.Lock acquireCaseResourcesLock(String caseDir)
Definition Case.java:1263
static PropertyChangeSupport getPropertyChangeSupport()
Definition Case.java:4052
static volatile Frame mainFrame
Definition Case.java:192
void deleteReports(Collection<? extends Report > reports)
Definition Case.java:1986
void notifyCentralRepoCommentChanged(long contentId, String newComment)
Definition Case.java:1875
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition Case.java:3926
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3481
void notifyContentTagAdded(ContentTag newTag)
Definition Case.java:1825
void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase)
Definition Case.java:2914
static final String CT_PROVIDER_PREFIX
Definition Case.java:185
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:2824
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
Definition Case.java:2638
void acquireCaseLock(CaseLockType lockType)
Definition Case.java:3233
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3299
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition Case.java:829
static boolean isNoNodeException(CoordinationServiceException ex)
Definition Case.java:3614
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition Case.java:3438
CollaborationMonitor collaborationMonitor
Definition Case.java:199
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition Case.java:1890
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
Definition Case.java:2618
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
Definition Case.java:2709
Image addImage(String imgPath, long imgId, String timeZone)
Definition Case.java:4085
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition Case.java:1801
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition Case.java:3579
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition Case.java:1914
void openCaseDataBase(ProgressIndicator progressIndicator)
Definition Case.java:2782
void openCaseLevelServices(ProgressIndicator progressIndicator)
Definition Case.java:2885
CoordinationService.Lock caseLock
Definition Case.java:196
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
Void create(ProgressIndicator progressIndicator, Object additionalParams)
Definition Case.java:2223
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:235
boolean equalsName(String otherTypeName)
Definition Case.java:293
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.