Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CommandLineOptionProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-2022 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  */
19 package org.sleuthkit.autopsy.commandlineingest;
20 
21 import java.beans.PropertyChangeListener;
22 import java.beans.PropertyChangeSupport;
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.logging.Level;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
34 import org.netbeans.api.sendopts.CommandException;
35 import org.netbeans.spi.sendopts.Env;
36 import org.netbeans.spi.sendopts.Option;
37 import org.netbeans.spi.sendopts.OptionProcessor;
38 import org.openide.util.lookup.ServiceProvider;
40 
44 @ServiceProvider(service = OptionProcessor.class)
45 public class CommandLineOptionProcessor extends OptionProcessor {
46 
47  private static final Logger logger = Logger.getLogger(CommandLineOptionProcessor.class.getName());
48  private final Option caseNameOption = Option.requiredArgument('n', "caseName");
49  private final Option caseTypeOption = Option.requiredArgument('t', "caseType");
50  private final Option caseBaseDirOption = Option.requiredArgument('o', "caseBaseDir");
51  private final Option createCaseCommandOption = Option.withoutArgument('c', "createCase");
52  private final Option dataSourcePathOption = Option.requiredArgument('s', "dataSourcePath");
53  private final Option dataSourceObjectIdOption = Option.requiredArgument('i', "dataSourceObjectId");
54  private final Option addDataSourceCommandOption = Option.withoutArgument('a', "addDataSource");
55  private final Option runIngestCommandOption = Option.optionalArgument('r', "runIngest");
56  private final Option listAllDataSourcesCommandOption = Option.withoutArgument('l', "listAllDataSources");
57  private final Option generateReportsOption = Option.optionalArgument('g', "generateReports");
58  private final Option listAllIngestProfileOption = Option.withoutArgument('p', "listAllIngestProfiles");
59  private final Option defaultArgument = Option.defaultArguments();
60 
61  private boolean runFromCommandLine = false;
62 
63  private final List<CommandLineCommand> commands = new ArrayList<>();
64 
65  final static String CASETYPE_MULTI = "multi";
66  final static String CASETYPE_SINGLE = "single";
67 
68  private String defaultArgumentValue = null;
69 
70  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
71  public static String PROCESSING_STARTED = "command line process started";
72  public static String PROCESSING_COMPLETED = "command line process completed";
73 
74  public enum ProcessState {
77  COMPLETED
78  }
79 
81 
82  @Override
83  protected Set<Option> getOptions() {
84  Set<Option> set = new HashSet<>();
85  set.add(createCaseCommandOption);
86  set.add(caseNameOption);
87  set.add(caseTypeOption);
88  set.add(caseBaseDirOption);
89  set.add(dataSourcePathOption);
90  set.add(addDataSourceCommandOption);
91  set.add(dataSourceObjectIdOption);
92  set.add(runIngestCommandOption);
93  set.add(listAllDataSourcesCommandOption);
94  set.add(generateReportsOption);
95  set.add(listAllIngestProfileOption);
96  set.add(defaultArgument);
97  return set;
98  }
99 
100  @Override
101  protected void process(Env env, Map<Option, String[]> values) throws CommandException {
102  logger.log(Level.INFO, "Processing Autopsy command line options"); //NON-NLS
103  System.out.println("Processing Autopsy command line options");
104  setState(ProcessState.RUNNING);
105  changes.firePropertyChange(PROCESSING_STARTED, false, true);
106 
107  if (values.containsKey(defaultArgument)) {
108  defaultArgumentValue = values.get(defaultArgument)[0];
109  runFromCommandLine(true);
110  return;
111  }
112 
113  // input arguments must contain at least one command
114  if (!(values.containsKey(createCaseCommandOption) || values.containsKey(addDataSourceCommandOption)
115  || values.containsKey(runIngestCommandOption) || values.containsKey(listAllDataSourcesCommandOption)
116  || values.containsKey(generateReportsOption) || values.containsKey(listAllIngestProfileOption))) {
117  // not running from command line
118  handleError("Invalid command line, an input option must be supplied.");
119  }
120 
121  // parse input parameters
122  String[] argDirs;
123  String inputCaseName = "";
124 
125  if(values.containsKey(listAllIngestProfileOption)) {
126  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.LIST_ALL_INGEST_PROFILES);
127  commands.add(newCommand);
128  runFromCommandLine(true);
129  } else {
130  if (values.containsKey(caseNameOption)) {
131  argDirs = values.get(caseNameOption);
132  if (argDirs.length < 1) {
133  handleError("Missing argument 'caseName'");
134  }
135  inputCaseName = argDirs[0];
136 
137  if (inputCaseName == null || inputCaseName.isEmpty()) {
138  handleError("'caseName' argument is empty");
139  }
140  }
141 
142  // 'caseName' must always be specified
143  if (inputCaseName == null || inputCaseName.isEmpty()) {
144  handleError("'caseName' argument is empty");
145  }
146 
147  String caseType = "";
148  if (values.containsKey(caseTypeOption)) {
149  argDirs = values.get(caseTypeOption);
150 
151  if (argDirs.length < 1) {
152  handleError("Missing argument 'caseType'");
153  }
154  caseType = argDirs[0];
155 
156  if (caseType == null || caseType.isEmpty()) {
157  handleError("'caseType' argument is empty");
158  } else if (!caseType.equalsIgnoreCase(CASETYPE_MULTI) && !caseType.equalsIgnoreCase(CASETYPE_SINGLE)) {
159  handleError("'caseType' argument is invalid");
160  } else if (caseType.equalsIgnoreCase(CASETYPE_MULTI) && !FeatureAccessUtils.canCreateMultiUserCases()) {
161  handleError("Unable to create multi user case. Confirm that multi user settings are configured correctly.");
162  }
163  }
164 
165  String caseBaseDir = "";
166  if (values.containsKey(caseBaseDirOption)) {
167  argDirs = values.get(caseBaseDirOption);
168  if (argDirs.length < 1) {
169  handleError("Missing argument 'caseBaseDir'");
170  }
171  caseBaseDir = argDirs[0];
172 
173  if (caseBaseDir == null || caseBaseDir.isEmpty()) {
174  handleError("Missing argument 'caseBaseDir' option");
175  }
176 
177  if (!(new File(caseBaseDir).exists()) || !(new File(caseBaseDir).isDirectory())) {
178  handleError("'caseBaseDir' directory doesn't exist or is not a directory: " + caseBaseDir);
179  }
180  }
181 
182  // 'caseBaseDir' must always be specified
183  if (caseBaseDir == null || caseBaseDir.isEmpty()) {
184  handleError("Missing argument 'caseBaseDir' option");
185  }
186 
187  String dataSourcePath = "";
188  if (values.containsKey(dataSourcePathOption)) {
189 
190  argDirs = values.get(dataSourcePathOption);
191  if (argDirs.length < 1) {
192  handleError("Missing argument 'dataSourcePath'");
193  }
194  dataSourcePath = argDirs[0];
195 
196  // verify inputs
197  if (dataSourcePath == null || dataSourcePath.isEmpty()) {
198  handleError("Missing argument 'dataSourcePath'");
199  }
200 
201  if (!(new File(dataSourcePath).exists())) {
202  handleError("Input data source file " + dataSourcePath + " doesn't exist");
203  }
204  }
205 
206  String dataSourceId = "";
207  if (values.containsKey(dataSourceObjectIdOption)) {
208 
209  argDirs = values.get(dataSourceObjectIdOption);
210  if (argDirs.length < 1) {
211  handleError("Missing argument 'dataSourceObjectIdOption'");
212  }
213  dataSourceId = argDirs[0];
214 
215  // verify inputs
216  if (dataSourceId == null || dataSourceId.isEmpty()) {
217  handleError("Input data source id is empty");
218  }
219  }
220 
221  // Create commands in order in which they should be executed:
222  // First create the "CREATE_CASE" command, if present
223  if (values.containsKey(createCaseCommandOption)) {
224 
225  // 'caseName' must always be specified for "CREATE_CASE" command
226  if (inputCaseName == null || inputCaseName.isEmpty()) {
227  handleError("'caseName' argument is empty");
228  }
229 
230  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.CREATE_CASE);
231  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
232  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
233  newCommand.addInputValue(CommandLineCommand.InputType.CASE_TYPE.name(), caseType);
234  commands.add(newCommand);
235  runFromCommandLine(true);
236  }
237 
238  // Add ADD_DATA_SOURCE command, if present
239  if (values.containsKey(addDataSourceCommandOption)) {
240 
241  // 'dataSourcePath' must always be specified for "ADD_DATA_SOURCE" command
242  if (dataSourcePath == null || dataSourcePath.isEmpty()) {
243  handleError("'dataSourcePath' argument is empty");
244  }
245 
246  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.ADD_DATA_SOURCE);
247  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
248  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
249  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath);
250  commands.add(newCommand);
251  runFromCommandLine(true);
252  }
253 
254  String ingestProfile = "";
255  // Add RUN_INGEST command, if present
256  if (values.containsKey(runIngestCommandOption)) {
257 
258  argDirs = values.get(runIngestCommandOption);
259  if(argDirs != null && argDirs.length > 0) {
260  ingestProfile = argDirs[0];
261  }
262 
263  // if new data source is being added during this run, then 'dataSourceId' is not specified
264  if (!values.containsKey(addDataSourceCommandOption) && dataSourceId.isEmpty()) {
265  // data source is not being added during this run, so 'dataSourceId' should have been specified
266  handleError("'dataSourceId' argument is empty");
267  }
268 
269  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.RUN_INGEST);
270  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
271  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
272  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_ID.name(), dataSourceId);
273  newCommand.addInputValue(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name(), ingestProfile);
274  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath);
275  commands.add(newCommand);
276  runFromCommandLine(true);
277  }
278 
279  // Add "LIST_ALL_DATA_SOURCES" command, if present
280  if (values.containsKey(listAllDataSourcesCommandOption)) {
281 
282  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.LIST_ALL_DATA_SOURCES);
283  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
284  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
285  commands.add(newCommand);
286  runFromCommandLine(true);
287  }
288 
289  // Add "GENERATE_REPORTS" command, if present
290  if (values.containsKey(generateReportsOption)) {
291  List<String> reportProfiles;
292  argDirs = values.get(generateReportsOption);
293  if (argDirs.length > 0) {
294  // use custom report configuration(s)
295  reportProfiles = Stream.of(argDirs[0].split(","))
296  .map(String::trim)
297  .collect(Collectors.toList());
298 
299  if (reportProfiles == null || reportProfiles.isEmpty()) {
300  handleError("'generateReports' argument is empty");
301  }
302 
303  for (String reportProfile : reportProfiles) {
304  if (reportProfile.isEmpty()) {
305  handleError("Empty report profile name");
306  }
307  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.GENERATE_REPORTS);
308  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
309  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
310  newCommand.addInputValue(CommandLineCommand.InputType.REPORT_PROFILE_NAME.name(), reportProfile);
311  commands.add(newCommand);
312  }
313  } else {
314  // use default report configuration
315  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.GENERATE_REPORTS);
316  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
317  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
318  commands.add(newCommand);
319  }
320 
321  runFromCommandLine(true);
322  }
323  }
324 
325  setState(ProcessState.COMPLETED);
326  System.out.println("Completed processing Autopsy command line options");
327  changes.firePropertyChange(PROCESSING_COMPLETED, false, true);
328  }
329 
335  public synchronized boolean isRunFromCommandLine() {
336  return runFromCommandLine;
337  }
338 
339  public synchronized void runFromCommandLine(boolean runFromCommandLine) {
340  this.runFromCommandLine = runFromCommandLine;
341  }
342 
348  public String getDefaultArgument() {
349  return defaultArgumentValue;
350  }
351 
357  List<CommandLineCommand> getCommands() {
358  return Collections.unmodifiableList(commands);
359  }
360 
368  private void handleError(String errorMessage) throws CommandException {
369  logger.log(Level.SEVERE, errorMessage);
370  throw new CommandException(CommandLineIngestManager.CL_PROCESS_FAILURE, errorMessage);
371  }
372 
374  PropertyChangeListener l) {
375  changes.addPropertyChangeListener(l);
376  }
378  PropertyChangeListener l) {
379  changes.removePropertyChangeListener(l);
380  }
381 
382  private synchronized void setState(ProcessState state) {
383  this.state = state;
384  }
385 
386  public synchronized ProcessState getState() {
387  return state;
388  }
389 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.