Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractIE.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2011-2016 Basis Technology Corp.
6  *
7  * Copyright 2012 42six Solutions.
8  * Contact: aebadirad <at> 42six <dot> com
9  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 package org.sleuthkit.autopsy.recentactivity;
24 
25 import java.io.BufferedReader;
26 
27 import org.openide.util.NbBundle;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.text.ParseException;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.HashSet;
40 import java.util.logging.Level;
42 import java.util.Collection;
43 import java.util.Scanner;
44 import java.util.stream.Collectors;
45 import org.openide.modules.InstalledFileLocator;
58 import org.sleuthkit.datamodel.*;
59 
64 class ExtractIE extends Extract {
65 
66  private static final Logger logger = Logger.getLogger(ExtractIE.class.getName());
67  private final IngestServices services = IngestServices.getInstance();
68  private final String moduleTempResultsDir;
69  private String PASCO_LIB_PATH;
70  private final String JAVA_PATH;
71  private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
72  private Content dataSource;
73  private IngestJobContext context;
74 
75  ExtractIE() {
76  moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractIE.moduleName.text");
77  moduleTempResultsDir = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), "IE") + File.separator + "results"; //NON-NLS
78  JAVA_PATH = PlatformUtil.getJavaPath();
79  }
80 
81  @Override
82  public void process(Content dataSource, IngestJobContext context) {
83  this.dataSource = dataSource;
84  this.context = context;
85  dataFound = false;
86  this.getBookmark();
87  this.getCookie();
88  this.getHistory();
89  }
90 
94  private void getBookmark() {
95  org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
96  List<AbstractFile> favoritesFiles;
97  try {
98  favoritesFiles = fileManager.findFiles(dataSource, "%.url", "Favorites"); //NON-NLS
99  } catch (TskCoreException ex) {
100  logger.log(Level.WARNING, "Error fetching 'url' files for Internet Explorer bookmarks.", ex); //NON-NLS
101  this.addErrorMessage(
102  NbBundle.getMessage(this.getClass(), "ExtractIE.getBookmark.errMsg.errGettingBookmarks",
103  this.getName()));
104  return;
105  }
106 
107  if (favoritesFiles.isEmpty()) {
108  logger.log(Level.INFO, "Didn't find any IE bookmark files."); //NON-NLS
109  return;
110  }
111 
112  dataFound = true;
113  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
114  for (AbstractFile fav : favoritesFiles) {
115  if (fav.getSize() == 0) {
116  continue;
117  }
118 
119  if (context.dataSourceIngestIsCancelled()) {
120  break;
121  }
122 
123  String url = getURLFromIEBookmarkFile(fav);
124 
125  String name = fav.getName();
126  Long datetime = fav.getCrtime();
127  String Tempdate = datetime.toString();
128  datetime = Long.valueOf(Tempdate);
129  String domain = Util.extractDomain(url);
130 
131  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
132  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
133  NbBundle.getMessage(this.getClass(),
134  "ExtractIE.parentModuleName.noSpace"), url));
135  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
136  NbBundle.getMessage(this.getClass(),
137  "ExtractIE.parentModuleName.noSpace"), name));
138  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
139  NbBundle.getMessage(this.getClass(),
140  "ExtractIE.parentModuleName.noSpace"), datetime));
141  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
142  NbBundle.getMessage(this.getClass(),
143  "ExtractIE.parentModuleName.noSpace"),
144  NbBundle.getMessage(this.getClass(), "ExtractIE.moduleName.text")));
145  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
146  NbBundle.getMessage(this.getClass(),
147  "ExtractIE.parentModuleName.noSpace"), domain));
148 
149  BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, fav, bbattributes);
150  if (bbart != null) {
151  bbartifacts.add(bbart);
152  }
153  }
154  services.fireModuleDataEvent(new ModuleDataEvent(
155  NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"),
157  }
158 
159  private String getURLFromIEBookmarkFile(AbstractFile fav) {
160  BufferedReader reader = new BufferedReader(new InputStreamReader(new ReadContentInputStream(fav)));
161  String line, url = "";
162  try {
163  line = reader.readLine();
164  while (null != line) {
165  // The actual shortcut line we are interested in is of the
166  // form URL=http://path/to/website
167  if (line.startsWith("URL")) { //NON-NLS
168  url = line.substring(line.indexOf("=") + 1);
169  break;
170  }
171  line = reader.readLine();
172  }
173  } catch (IOException ex) {
174  logger.log(Level.WARNING, "Failed to read from content: " + fav.getName(), ex); //NON-NLS
175  this.addErrorMessage(
176  NbBundle.getMessage(this.getClass(), "ExtractIE.getURLFromIEBmkFile.errMsg", this.getName(),
177  fav.getName()));
178  } catch (IndexOutOfBoundsException ex) {
179  logger.log(Level.WARNING, "Failed while getting URL of IE bookmark. Unexpected format of the bookmark file: " + fav.getName(), ex); //NON-NLS
180  this.addErrorMessage(
181  NbBundle.getMessage(this.getClass(), "ExtractIE.getURLFromIEBmkFile.errMsg2", this.getName(),
182  fav.getName()));
183  } finally {
184  try {
185  reader.close();
186  } catch (IOException ex) {
187  logger.log(Level.WARNING, "Failed to close reader.", ex); //NON-NLS
188  }
189  }
190 
191  return url;
192  }
193 
197  private void getCookie() {
198  org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
199  List<AbstractFile> cookiesFiles;
200  try {
201  cookiesFiles = fileManager.findFiles(dataSource, "%.txt", "Cookies"); //NON-NLS
202  } catch (TskCoreException ex) {
203  logger.log(Level.WARNING, "Error getting cookie files for IE"); //NON-NLS
204  this.addErrorMessage(
205  NbBundle.getMessage(this.getClass(), "ExtractIE.getCookie.errMsg.errGettingFile", this.getName()));
206  return;
207  }
208 
209  if (cookiesFiles.isEmpty()) {
210  logger.log(Level.INFO, "Didn't find any IE cookies files."); //NON-NLS
211  return;
212  }
213 
214  dataFound = true;
215  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
216  for (AbstractFile cookiesFile : cookiesFiles) {
217  if (context.dataSourceIngestIsCancelled()) {
218  break;
219  }
220  if (cookiesFile.getSize() == 0) {
221  continue;
222  }
223 
224  byte[] t = new byte[(int) cookiesFile.getSize()];
225  try {
226  final int bytesRead = cookiesFile.read(t, 0, cookiesFile.getSize());
227  } catch (TskCoreException ex) {
228  logger.log(Level.WARNING, "Error reading bytes of Internet Explorer cookie.", ex); //NON-NLS
229  this.addErrorMessage(
230  NbBundle.getMessage(this.getClass(), "ExtractIE.getCookie.errMsg.errReadingIECookie",
231  this.getName(), cookiesFile.getName()));
232  continue;
233  }
234  String cookieString = new String(t);
235  String[] values = cookieString.split("\n");
236  String url = values.length > 2 ? values[2] : "";
237  String value = values.length > 1 ? values[1] : "";
238  String name = values.length > 0 ? values[0] : "";
239  Long datetime = cookiesFile.getCrtime();
240  String tempDate = datetime.toString();
241  datetime = Long.valueOf(tempDate);
242  String domain = Util.extractDomain(url);
243 
244  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
245  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
246  NbBundle.getMessage(this.getClass(),
247  "ExtractIE.parentModuleName.noSpace"), url));
248  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
249  NbBundle.getMessage(this.getClass(),
250  "ExtractIE.parentModuleName.noSpace"), datetime));
251  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
252  NbBundle.getMessage(this.getClass(),
253  "ExtractIE.parentModuleName.noSpace"), (name != null) ? name : ""));
254  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
255  NbBundle.getMessage(this.getClass(),
256  "ExtractIE.parentModuleName.noSpace"), value));
257  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
258  NbBundle.getMessage(this.getClass(),
259  "ExtractIE.parentModuleName.noSpace"),
260  NbBundle.getMessage(this.getClass(), "ExtractIE.moduleName.text")));
261  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
262  NbBundle.getMessage(this.getClass(),
263  "ExtractIE.parentModuleName.noSpace"), domain));
264  BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
265  if (bbart != null) {
266  bbartifacts.add(bbart);
267  }
268  }
269  services.fireModuleDataEvent(new ModuleDataEvent(
270  NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"),
272  }
273 
277  private void getHistory() {
278  logger.log(Level.INFO, "Pasco results path: {0}", moduleTempResultsDir); //NON-NLS
279  boolean foundHistory = false;
280 
281  final File pascoRoot = InstalledFileLocator.getDefault().locate("pasco2", ExtractIE.class.getPackage().getName(), false); //NON-NLS
282  if (pascoRoot == null) {
283  this.addErrorMessage(
284  NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.unableToGetHist", this.getName()));
285  logger.log(Level.SEVERE, "Error finding pasco program "); //NON-NLS
286  return;
287  }
288 
289  final String pascoHome = pascoRoot.getAbsolutePath();
290  logger.log(Level.INFO, "Pasco2 home: {0}", pascoHome); //NON-NLS
291 
292  PASCO_LIB_PATH = pascoHome + File.separator + "pasco2.jar" + File.pathSeparator //NON-NLS
293  + pascoHome + File.separator + "*";
294 
295  File resultsDir = new File(moduleTempResultsDir);
296  resultsDir.mkdirs();
297 
298  // get index.dat files
299  org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
300  List<AbstractFile> indexFiles;
301  try {
302  indexFiles = fileManager.findFiles(dataSource, "index.dat"); //NON-NLS
303  } catch (TskCoreException ex) {
304  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errGettingHistFiles",
305  this.getName()));
306  logger.log(Level.WARNING, "Error fetching 'index.data' files for Internet Explorer history."); //NON-NLS
307  return;
308  }
309 
310  if (indexFiles.isEmpty()) {
311  String msg = NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.noHistFiles");
312  logger.log(Level.INFO, msg);
313  return;
314  }
315 
316  dataFound = true;
317  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
318  String temps;
319  String indexFileName;
320  for (AbstractFile indexFile : indexFiles) {
321  // Since each result represent an index.dat file,
322  // just create these files with the following notation:
323  // index<Number>.dat (i.e. index0.dat, index1.dat,..., indexN.dat)
324  // Write each index.dat file to a temp directory.
325  //BlackboardArtifact bbart = fsc.newArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY);
326  indexFileName = "index" + Integer.toString((int) indexFile.getId()) + ".dat"; //NON-NLS
327  //indexFileName = "index" + Long.toString(bbart.getArtifactID()) + ".dat";
328  temps = RAImageIngestModule.getRATempPath(currentCase, "IE") + File.separator + indexFileName; //NON-NLS
329  File datFile = new File(temps);
330  if (context.dataSourceIngestIsCancelled()) {
331  break;
332  }
333  try {
334  ContentUtils.writeToFile(indexFile, datFile, context::dataSourceIngestIsCancelled);
335  } catch (IOException e) {
336  logger.log(Level.WARNING, "Error while trying to write index.dat file " + datFile.getAbsolutePath(), e); //NON-NLS
337  this.addErrorMessage(
338  NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errWriteFile", this.getName(),
339  datFile.getAbsolutePath()));
340  continue;
341  }
342 
343  String filename = "pasco2Result." + indexFile.getId() + ".txt"; //NON-NLS
344  boolean bPascProcSuccess = executePasco(temps, filename);
345  if (context.dataSourceIngestIsCancelled()) {
346  return;
347  }
348 
349  //At this point pasco2 proccessed the index files.
350  //Now fetch the results, parse them and the delete the files.
351  if (bPascProcSuccess) {
352  // Don't add TSK_OS_ACCOUNT artifacts to the ModuleDataEvent
353  bbartifacts.addAll(parsePascoOutput(indexFile, filename).stream()
354  .filter(bbart -> bbart.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID())
355  .collect(Collectors.toList()));
356  foundHistory = true;
357 
358  //Delete index<n>.dat file since it was succcessfully by Pasco
359  datFile.delete();
360  } else {
361  logger.log(Level.WARNING, "pasco execution failed on: {0}", this.getName()); //NON-NLS
362  this.addErrorMessage(
363  NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errProcHist", this.getName()));
364  }
365  }
366 
367  if (foundHistory) {
368  services.fireModuleDataEvent(new ModuleDataEvent(
369  NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName"),
371  }
372  }
373 
382  private boolean executePasco(String indexFilePath, String outputFileName) {
383  boolean success = true;
384  try {
385  final String outputFileFullPath = moduleTempResultsDir + File.separator + outputFileName;
386  final String errFileFullPath = moduleTempResultsDir + File.separator + outputFileName + ".err"; //NON-NLS
387  logger.log(Level.INFO, "Writing pasco results to: {0}", outputFileFullPath); //NON-NLS
388  List<String> commandLine = new ArrayList<>();
389  commandLine.add(JAVA_PATH);
390  commandLine.add("-cp"); //NON-NLS
391  commandLine.add(PASCO_LIB_PATH);
392  commandLine.add("isi.pasco2.Main"); //NON-NLS
393  commandLine.add("-T"); //NON-NLS
394  commandLine.add("history"); //NON-NLS
395  commandLine.add(indexFilePath);
396  ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
397  processBuilder.redirectOutput(new File(outputFileFullPath));
398  processBuilder.redirectError(new File(errFileFullPath));
399  /*
400  * NOTE on Pasco return codes: There is no documentation for Pasco.
401  * Looking at the Pasco source code I see that when something goes
402  * wrong Pasco returns a negative number as a return code. However,
403  * we should still attempt to parse the Pasco output even if that
404  * happens. I have seen many situations where Pasco output file
405  * contains a lot of useful data and only the last entry is
406  * corrupted.
407  */
408  ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context));
409  // @@@ Investigate use of history versus cache as type.
410  } catch (IOException ex) {
411  success = false;
412  logger.log(Level.SEVERE, "Unable to execute Pasco to process Internet Explorer web history.", ex); //NON-NLS
413  }
414  return success;
415  }
416 
426  private Collection<BlackboardArtifact> parsePascoOutput(AbstractFile origFile, String pascoOutputFileName) {
427 
428  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
429  String fnAbs = moduleTempResultsDir + File.separator + pascoOutputFileName;
430 
431  File file = new File(fnAbs);
432  if (file.exists() == false) {
433  this.addErrorMessage(
434  NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.notFound", this.getName(),
435  file.getName()));
436  logger.log(Level.WARNING, "Pasco Output not found: {0}", file.getPath()); //NON-NLS
437  return bbartifacts;
438  }
439 
440  // Make sure the file the is not empty or the Scanner will
441  // throw a "No Line found" Exception
442  if (file.length() == 0) {
443  return bbartifacts;
444  }
445 
446  Scanner fileScanner;
447  try {
448  fileScanner = new Scanner(new FileInputStream(file.toString()));
449  } catch (FileNotFoundException ex) {
450  this.addErrorMessage(
451  NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.errParsing", this.getName(),
452  file.getName()));
453  logger.log(Level.WARNING, "Unable to find the Pasco file at " + file.getPath(), ex); //NON-NLS
454  return bbartifacts;
455  }
456 
457  // Keep a list of reported user accounts to avoid repeats
458  Set<String> reportedUserAccounts = new HashSet<>();
459 
460  while (fileScanner.hasNext()) {
461  String line = fileScanner.nextLine();
462  if (!line.startsWith("URL")) { //NON-NLS
463  continue;
464  }
465 
466  String[] lineBuff = line.split("\\t"); //NON-NLS
467 
468  if (lineBuff.length < 4) {
469  logger.log(Level.INFO, "Found unrecognized IE history format."); //NON-NLS
470  continue;
471  }
472 
473  String actime = lineBuff[3];
474  Long ftime = (long) 0;
475  String user;
476  String realurl;
477  String domain;
478 
479  /*
480  * We've seen two types of lines: URL http://XYZ.com .... URL
481  * Visited: Joe@http://XYZ.com ....
482  */
483  if (lineBuff[1].contains("@")) {
484  String url[] = lineBuff[1].split("@", 2);
485  user = url[0];
486  user = user.replace("Visited:", ""); //NON-NLS
487  user = user.replace(":Host:", ""); //NON-NLS
488  user = user.replaceAll("(:)(.*?)(:)", "");
489  user = user.trim();
490  realurl = url[1];
491  realurl = realurl.replace("Visited:", ""); //NON-NLS
492  realurl = realurl.replaceAll(":(.*?):", "");
493  realurl = realurl.replace(":Host:", ""); //NON-NLS
494  realurl = realurl.trim();
495  } else {
496  user = "";
497  realurl = lineBuff[1].trim();
498  }
499 
500  domain = Util.extractDomain(realurl);
501 
502  if (!actime.isEmpty()) {
503  try {
504  Long epochtime = dateFormatter.parse(actime).getTime();
505  ftime = epochtime / 1000;
506  } catch (ParseException e) {
507  this.addErrorMessage(
508  NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.errParsingEntry",
509  this.getName()));
510  logger.log(Level.WARNING, String.format("Error parsing Pasco results, may have partial processing of corrupt file (id=%d)", origFile.getId()), e); //NON-NLS
511  }
512  }
513 
514  try {
515  BlackboardArtifact bbart = origFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY);
516  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
517  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
518  NbBundle.getMessage(this.getClass(),
519  "ExtractIE.parentModuleName.noSpace"), realurl));
520  //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(realurl)));
521 
522  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
523  NbBundle.getMessage(this.getClass(),
524  "ExtractIE.parentModuleName.noSpace"), ftime));
525  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
526  NbBundle.getMessage(this.getClass(),
527  "ExtractIE.parentModuleName.noSpace"), ""));
528  // @@@ NOte that other browser modules are adding TITLE in hre for the title
529  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
530  NbBundle.getMessage(this.getClass(),
531  "ExtractIE.parentModuleName.noSpace"),
532  NbBundle.getMessage(this.getClass(),
533  "ExtractIE.moduleName.text")));
534  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
535  NbBundle.getMessage(this.getClass(),
536  "ExtractIE.parentModuleName.noSpace"), domain));
537  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
538  NbBundle.getMessage(this.getClass(),
539  "ExtractIE.parentModuleName.noSpace"), user));
540  bbart.addAttributes(bbattributes);
541 
542  // index the artifact for keyword search
543  this.indexArtifact(bbart);
544  bbartifacts.add(bbart);
545 
546  if ((!user.isEmpty()) && (!reportedUserAccounts.contains(user))) {
547  BlackboardArtifact osAttr = origFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
548  osAttr.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
549  NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName.noSpace"), user));
550 
551  // index the artifact for keyword search
552  this.indexArtifact(osAttr);
553  bbartifacts.add(osAttr);
554 
555  reportedUserAccounts.add(user);
556  }
557  } catch (TskCoreException ex) {
558  logger.log(Level.SEVERE, "Error writing Internet Explorer web history artifact to the blackboard.", ex); //NON-NLS
559  }
560  }
561  fileScanner.close();
562  return bbartifacts;
563  }
564 }
void addAttributes(Collection< BlackboardAttribute > attributes)
void addAttribute(BlackboardAttribute attr)
BlackboardArtifact newArtifact(int artifactTypeID)
synchronized List< AbstractFile > findFiles(String fileName)
String toString(boolean preserveState)

Copyright © 2012-2016 Basis Technology. Generated on: Mon Apr 24 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.