Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
KeywordSearchList.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2016 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.keywordsearch;
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.Date;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.logging.Level;
30 import org.openide.util.NbBundle;
33 import org.sleuthkit.datamodel.BlackboardAttribute;
34 
38 abstract class KeywordSearchList {
39 
40  protected static final Logger LOGGER = Logger.getLogger(KeywordSearchList.class.getName());
41 
42  // These are the valid characters that can appear either before or after a
43  // keyword hit. We use these characters to try to reduce the number of false
44  // positives for phone numbers and IP addresses. They don't work as well
45  // for "string" types such as URLs since the characters are more likely to
46  // appear in the resulting hit.
47  static final String BOUNDARY_CHARACTERS = "[ \t\r\n\\.\\-\\?\\,\\;\\\\!\\:\\[\\]\\/\\(\\)\\\"\\\'\\>\\{\\}]";
48  private static final String PHONE_NUMBER_REGEX = BOUNDARY_CHARACTERS + "(\\([0-9]{3}\\)|[0-9]{3})([ \\-\\.])[0-9]{3}([ \\-\\.])[0-9]{4}" + BOUNDARY_CHARACTERS; //NON-NLS
49  private static final String IP_ADDRESS_REGEX = BOUNDARY_CHARACTERS + "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(1[0-9]{2}|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])" + BOUNDARY_CHARACTERS; //NON-NLS
50  private static final String EMAIL_ADDRESS_REGEX = "(\\{?)[a-zA-Z0-9%+_\\-]+(\\.[a-zA-Z0-9%+_\\-]+)*(\\}?)\\@([a-zA-Z0-9]([a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,4}"; //NON-NLS
51  private static final String URL_REGEX = "(((((h|H)(t|T))|(f|F))(t|T)(p|P)(s|S?)\\:\\/\\/)|(w|W){3,3}\\.)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,5})(\\:[0-9]+)*(\\/($|[a-zA-Z0-9\\.\\,\\;\\?\\'\\\\+&amp;%\\$#\\=~_\\-]+))*"; //NON-NLS
52 
61  private static final String CCN_REGEX = "(%?)(B?)([0-9][ \\-]*?){12,19}(\\^?)"; //NON-NLS
62 
63  protected String filePath;
64  Map<String, KeywordList> theLists; //the keyword data
65 
66  PropertyChangeSupport changeSupport;
67  protected List<String> lockedLists;
68 
69  KeywordSearchList(String filePath) {
70  this.filePath = filePath;
71  theLists = new LinkedHashMap<>();
72  lockedLists = new ArrayList<>();
73  changeSupport = new PropertyChangeSupport(this);
74  }
75 
81  enum ListsEvt {
82 
83  LIST_ADDED,
84  LIST_DELETED,
85  LIST_UPDATED
86  };
87 
88  enum LanguagesEvent {
89 
90  LANGUAGES_CHANGED,
91  ENCODINGS_CHANGED
92  }
93 
94  void fireLanguagesEvent(LanguagesEvent event) {
95  try {
96  changeSupport.firePropertyChange(event.toString(), null, null);
97  } catch (Exception e) {
98  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
99  }
100  }
101 
102  public void addPropertyChangeListener(PropertyChangeListener listener) {
103  changeSupport.addPropertyChangeListener(listener);
104  }
105 
106  public void removePropertyChangeListener(PropertyChangeListener listener) {
107  changeSupport.removePropertyChangeListener(listener);
108  }
109 
110  private void prepopulateLists() {
111  if (!theLists.isEmpty()) {
112  return;
113  }
114  //phone number
115  List<Keyword> phones = new ArrayList<>();
116  phones.add(new Keyword(PHONE_NUMBER_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER));
117  lockedLists.add("Phone Numbers");
118  addList("Phone Numbers", phones, false, false, true);
119 
120  //IP address
121  List<Keyword> ips = new ArrayList<>();
122  ips.add(new Keyword(IP_ADDRESS_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IP_ADDRESS));
123  lockedLists.add("IP Addresses");
124  addList("IP Addresses", ips, false, false, true);
125 
126  //email
127  List<Keyword> emails = new ArrayList<>();
128  emails.add(new Keyword(EMAIL_ADDRESS_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL));
129  lockedLists.add("Email Addresses");
130  addList("Email Addresses", emails, true, false, true);
131 
132  //URL
133  List<Keyword> urls = new ArrayList<>();
134  urls.add(new Keyword(URL_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL));
135  lockedLists.add("URLs");
136  addList("URLs", urls, false, false, true);
137 
138  //CCN
139  List<Keyword> ccns = new ArrayList<>();
140  ccns.add(new Keyword(CCN_REGEX, false, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
141  lockedLists.add("Credit Card Numbers");
142  addList("Credit Card Numbers", ccns, false, false, true);
143  }
144 
148  public void reload() {
149  boolean created = false;
150 
151  //theLists.clear();
152  //populate only the first time
153  prepopulateLists();
154 
155  //reset all the lists other than locked lists (we don't save them to XML)
156  //we want to preserve state of locked lists
157  List<String> toClear = new ArrayList<>();
158  for (String list : theLists.keySet()) {
159  if (theLists.get(list).isEditable() == false) {
160  toClear.add(list);
161  }
162  }
163  for (String clearList : toClear) {
164  theLists.remove(clearList);
165  }
166 
167  if (!listFileExists()) {
168  //create new if it doesn't exist
169  save();
170  created = true;
171  }
172 
173  //load, if fails to load create new
174  if (!load() && !created) {
175  //create new if failed to load
176  save();
177  }
178  }
179 
180  public List<KeywordList> getListsL() {
181  List<KeywordList> ret = new ArrayList<>();
182  for (KeywordList list : theLists.values()) {
183  ret.add(list);
184  }
185  return ret;
186  }
187 
188  public List<KeywordList> getListsL(boolean locked) {
189  List<KeywordList> ret = new ArrayList<>();
190  for (KeywordList list : theLists.values()) {
191  if (list.isEditable().equals(locked)) {
192  ret.add(list);
193  }
194  }
195  return ret;
196  }
197 
203  public List<String> getListNames() {
204  return new ArrayList<>(theLists.keySet());
205  }
206 
214  public List<String> getListNames(boolean locked) {
215  ArrayList<String> lists = new ArrayList<>();
216  for (String listName : theLists.keySet()) {
217  KeywordList list = theLists.get(listName);
218  if (locked == list.isEditable()) {
219  lists.add(listName);
220  }
221  }
222 
223  return lists;
224  }
225 
233  public KeywordList getListWithKeyword(String keyword) {
234  KeywordList found = null;
235  for (KeywordList list : theLists.values()) {
236  if (list.hasSearchTerm(keyword)) {
237  found = list;
238  break;
239  }
240  }
241  return found;
242  }
243 
249  int getNumberLists() {
250  return theLists.size();
251  }
252 
260  public int getNumberLists(boolean locked) {
261  int numLists = 0;
262  for (String listName : theLists.keySet()) {
263  KeywordList list = theLists.get(listName);
264  if (locked == list.isEditable()) {
265  ++numLists;
266  }
267  }
268  return numLists;
269  }
270 
278  public KeywordList getList(String name) {
279  return theLists.get(name);
280  }
281 
289  boolean listExists(String name) {
290  return getList(name) != null;
291  }
292 
303  boolean addList(String name, List<Keyword> newList, boolean useForIngest, boolean ingestMessages, boolean locked) {
304  boolean replaced = false;
305  KeywordList curList = getList(name);
306  final Date now = new Date();
307 
308  if (curList == null) {
309  theLists.put(name, new KeywordList(name, now, now, useForIngest, ingestMessages, newList, locked));
310  try {
311  changeSupport.firePropertyChange(ListsEvt.LIST_ADDED.toString(), null, name);
312  } catch (Exception e) {
313  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
314  MessageNotifyUtil.Notify.show(
315  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
316  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.addList.errMsg1.msg"),
317  MessageNotifyUtil.MessageType.ERROR);
318  }
319  } else {
320  theLists.put(name, new KeywordList(name, curList.getDateCreated(), now, useForIngest, ingestMessages, newList, locked));
321  replaced = true;
322 
323  try {
324  changeSupport.firePropertyChange(ListsEvt.LIST_UPDATED.toString(), null, name);
325  } catch (Exception e) {
326  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
327  MessageNotifyUtil.Notify.show(
328  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
329  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.addList.errMsg2.msg"),
330  MessageNotifyUtil.MessageType.ERROR);
331  }
332  }
333 
334  return replaced;
335  }
336 
337  boolean addList(String name, List<Keyword> newList, boolean useForIngest, boolean ingestMessages) {
338  //make sure that the list is readded as a locked/built in list
339  boolean isLocked = this.lockedLists.contains(name);
340  return addList(name, newList, useForIngest, ingestMessages, isLocked);
341  }
342 
343  boolean addList(String name, List<Keyword> newList) {
344  return addList(name, newList, true, true);
345  }
346 
347  boolean addList(KeywordList list) {
348  return addList(list.getName(), list.getKeywords(), list.getUseForIngest(), list.getIngestMessages(), list.isEditable());
349  }
350 
358  boolean saveLists(List<KeywordList> lists) {
359  List<KeywordList> overwritten = new ArrayList<>();
360  List<KeywordList> newLists = new ArrayList<>();
361  for (KeywordList list : lists) {
362  if (this.listExists(list.getName())) {
363  overwritten.add(list);
364  } else {
365  newLists.add(list);
366  }
367  theLists.put(list.getName(), list);
368  }
369  boolean saved = save(true);
370  if (saved) {
371  for (KeywordList list : newLists) {
372  try {
373  changeSupport.firePropertyChange(ListsEvt.LIST_ADDED.toString(), null, list.getName());
374  } catch (Exception e) {
375  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
376  MessageNotifyUtil.Notify.show(
377  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
378  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.saveList.errMsg1.msg"),
379  MessageNotifyUtil.MessageType.ERROR);
380  }
381  }
382  for (KeywordList over : overwritten) {
383  try {
384  changeSupport.firePropertyChange(ListsEvt.LIST_UPDATED.toString(), null, over.getName());
385  } catch (Exception e) {
386  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
387  MessageNotifyUtil.Notify.show(
388  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
389  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.saveList.errMsg2.msg"),
390  MessageNotifyUtil.MessageType.ERROR);
391  }
392  }
393  }
394 
395  return saved;
396  }
397 
405  boolean writeLists(List<KeywordList> lists) {
406  List<KeywordList> overwritten = new ArrayList<>();
407  List<KeywordList> newLists = new ArrayList<>();
408  for (KeywordList list : lists) {
409  if (this.listExists(list.getName())) {
410  overwritten.add(list);
411  } else {
412  newLists.add(list);
413  }
414  theLists.put(list.getName(), list);
415  }
416 
417  for (KeywordList list : newLists) {
418 
419  try {
420  changeSupport.firePropertyChange(ListsEvt.LIST_ADDED.toString(), null, list.getName());
421  } catch (Exception e) {
422  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
423  MessageNotifyUtil.Notify.show(
424  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
425  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.writeLists.errMsg1.msg"),
426  MessageNotifyUtil.MessageType.ERROR);
427  }
428  }
429 
430  for (KeywordList over : overwritten) {
431 
432  try {
433  changeSupport.firePropertyChange(ListsEvt.LIST_UPDATED.toString(), null, over.getName());
434  } catch (Exception e) {
435  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
436  MessageNotifyUtil.Notify.show(
437  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
438  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.writeLists.errMsg2.msg"),
439  MessageNotifyUtil.MessageType.ERROR);
440  }
441  }
442 
443  return true;
444  }
445 
453  boolean deleteList(String name) {
454  KeywordList delList = getList(name);
455  if (delList != null && !delList.isEditable()) {
456  theLists.remove(name);
457  }
458 
459  try {
460  changeSupport.firePropertyChange(ListsEvt.LIST_DELETED.toString(), null, name);
461  } catch (Exception e) {
462  LOGGER.log(Level.SEVERE, "KeywordSearchListsAbstract listener threw exception", e); //NON-NLS
463  MessageNotifyUtil.Notify.show(
464  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.moduleErr"),
465  NbBundle.getMessage(this.getClass(), "KeywordSearchListsAbstract.deleteList.errMsg1.msg"),
466  MessageNotifyUtil.MessageType.ERROR);
467  }
468 
469  return true;
470  }
471 
475  public abstract boolean save();
476 
483  public abstract boolean save(boolean isExport);
484 
488  public abstract boolean load();
489 
490  private boolean listFileExists() {
491  File f = new File(filePath);
492  return f.exists() && f.canRead() && f.canWrite();
493  }
494 
495  public void setUseForIngest(String key, boolean flag) {
496  theLists.get(key).setUseForIngest(flag);
497  }
498 }

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.