Autopsy  4.19.3
Graphical digital forensics platform for The Sleuth Kit and other tools.
FileTypesByMimeType.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2019 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.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Observable;
32 import java.util.Observer;
33 import java.util.Set;
34 import java.util.logging.Level;
35 import java.util.stream.Collectors;
36 import org.apache.commons.lang3.StringUtils;
37 import org.openide.nodes.ChildFactory;
38 import org.openide.nodes.Children;
39 import org.openide.nodes.Node;
40 import org.openide.nodes.Sheet;
41 import org.openide.util.NbBundle;
42 import org.openide.util.lookup.Lookups;
50 import org.sleuthkit.datamodel.SleuthkitCase;
51 import org.sleuthkit.datamodel.TskCoreException;
52 import org.sleuthkit.datamodel.TskData;
54 
62 public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
63 
64  private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
66 
72  private final HashMap<String, Map<String, Long>> existingMimeTypeCounts = new HashMap<>();
77  private final FileTypes typesRoot;
78 
83  private final PropertyChangeListener pcl;
84 
86 
92 
101  private String createBaseWhereExpr() {
102  return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
103  + " AND (type IN ("
104  + TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
105  + TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
106  + TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
107  + TskData.TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.ordinal() + ","
108  + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
109  + (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
110  + "))"
111  + ((filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
112  + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
113  }
114 
115  private void removeListeners() {
116  deleteObservers();
118  refreshThrottler.unregisterEventListener();
120  }
121 
126  private void populateHashMap() {
127  String query = "SELECT mime_type, count(*) AS count FROM tsk_files "
128  + " WHERE mime_type IS NOT null "
129  + " AND " + createBaseWhereExpr()
130  + " GROUP BY mime_type";
131  synchronized (existingMimeTypeCounts) {
132  existingMimeTypeCounts.clear();
133  try
134  (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
135  ResultSet resultSet = dbQuery.getResultSet();
136  while (resultSet.next()) {
137  final String mime_type = resultSet.getString("mime_type"); //NON-NLS
138  if (!mime_type.isEmpty()) {
139  //if the mime_type contained multiple slashes then everything after the first slash will become the subtype
140  final String mediaType = StringUtils.substringBefore(mime_type, "/");
141  final String subType = StringUtils.removeStart(mime_type, mediaType + "/");
142  if (!mediaType.isEmpty() && !subType.isEmpty()) {
143  final long count = resultSet.getLong("count");
144  existingMimeTypeCounts.computeIfAbsent(mediaType, t -> new HashMap<>())
145  .put(subType, count);
146  }
147  }
148  }
149  } catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
150  logger.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
151  }
152  }
153 
154  setChanged();
155  notifyObservers();
156  }
157 
158  FileTypesByMimeType(FileTypes typesRoot) {
159  this.typesRoot = typesRoot;
160  this.pcl = (PropertyChangeEvent evt) -> {
161  String eventType = evt.getPropertyName();
162  if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
163  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
164  || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
165 
167  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
168  if (evt.getNewValue() == null) {
169  removeListeners();
170  }
171  }
172  };
173  refreshThrottler = new RefreshThrottler(new FileTypesByMimeTypeRefresher());
174  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
175  refreshThrottler.registerForIngestModuleEvents();
176  Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
177  populateHashMap();
178  }
179 
180  @Override
181  public <T> T accept(AutopsyItemVisitor<T> visitor) {
182  return visitor.visit(this);
183  }
184 
185  long filteringDataSourceObjId() {
186  return typesRoot.filteringDataSourceObjId();
187  }
188 
198  public static boolean isEmptyMimeTypeNode(Node node) {
199  boolean isEmptyMimeNode = false;
200  if (node instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) node).isEmpty()) {
201  isEmptyMimeNode = true;
202  }
203  return isEmptyMimeNode;
204 
205  }
206 
207  private void refreshMimeTypes() {
213  try {
215  typesRoot.updateShowCounts();
216  populateHashMap();
217  } catch (NoCurrentCaseException notUsed) {
221  }
222  }
223 
228  private class FileTypesByMimeTypeRefresher implements RefreshThrottler.Refresher {
229 
230  @Override
231  public void refresh() {
233  }
234 
235  @Override
236  public boolean isRefreshRequired(PropertyChangeEvent evt) {
237  return true;
238  }
239 
240  }
241 
248  class ByMimeTypeNode extends DisplayableItemNode {
249 
250  @NbBundle.Messages({"FileTypesByMimeType.name.text=By MIME Type"})
251 
252  final String NAME = Bundle.FileTypesByMimeType_name_text();
253 
254  ByMimeTypeNode() {
255  super(Children.create(new ByMimeTypeNodeChildren(), true), Lookups.singleton(Bundle.FileTypesByMimeType_name_text()));
256  super.setName(NAME);
257  super.setDisplayName(NAME);
258  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
259  }
260 
261  @Override
262  public boolean isLeafTypeNode() {
263  return false;
264  }
265 
266  @Override
267  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
268  return visitor.visit(this);
269  }
270 
271  @Override
272  public String getItemType() {
273  return getClass().getName();
274  }
275 
276  boolean isEmpty() {
277  synchronized (existingMimeTypeCounts) {
278  return existingMimeTypeCounts.isEmpty();
279  }
280  }
281  }
282 
287  private class ByMimeTypeNodeChildren extends ChildFactory<String> implements Observer {
288 
290  super();
291  addObserver(this);
292  }
293 
294  @Override
295  protected boolean createKeys(List<String> mediaTypeNodes) {
296  final List<String> keylist;
297  synchronized (existingMimeTypeCounts) {
298  keylist = new ArrayList<>(existingMimeTypeCounts.keySet());
299  }
300  Collections.sort(keylist);
301  mediaTypeNodes.addAll(keylist);
302 
303  return true;
304  }
305 
306  @Override
307  protected Node createNodeForKey(String key) {
308  return new MediaTypeNode(key);
309  }
310 
311  @Override
312  public void update(Observable o, Object arg) {
313  refresh(true);
314  }
315  }
316 
321  class MediaTypeNode extends DisplayableItemNode {
322 
323  @NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaType.name=Type",
324  "FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type",
325  "FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description"})
326 
327  MediaTypeNode(String name) {
328  super(Children.create(new MediaTypeNodeChildren(name), true), Lookups.singleton(name));
329  setName(name);
330  setDisplayName(name);
331  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
332  }
333 
334  @Override
335  public boolean isLeafTypeNode() {
336  return false;
337  }
338 
339  @Override
340  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
341  return visitor.visit(this);
342  }
343 
344  @Override
345  protected Sheet createSheet() {
346  Sheet sheet = super.createSheet();
347  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
348  if (sheetSet == null) {
349  sheetSet = Sheet.createPropertiesSet();
350  sheet.put(sheetSet);
351  }
352  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.desc"), getDisplayName()));
353  return sheet;
354  }
355 
356  @Override
357  public String getItemType() {
358  return getClass().getName();
359  }
360 
361  }
362 
368  private class MediaTypeNodeChildren extends ChildFactory<String> implements Observer {
369 
370  String mediaType;
371 
372  MediaTypeNodeChildren(String name) {
373  addObserver(this);
374  this.mediaType = name;
375  }
376 
377  @Override
378  protected boolean createKeys(List<String> mediaTypeNodes) {
379  mediaTypeNodes.addAll(existingMimeTypeCounts.get(mediaType).keySet());
380  return true;
381  }
382 
383  @Override
384  protected Node createNodeForKey(String subtype) {
385  String mimeType = mediaType + "/" + subtype;
386  return new MediaSubTypeNode(mimeType);
387  }
388 
389  @Override
390  public void update(Observable o, Object arg) {
391  refresh(true);
392  }
393 
394  }
395 
400  final class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
401 
402  @NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype",
403  "FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=Subtype",
404  "FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc=no description"})
405  private final String mimeType;
406  private final String subType;
407 
408  private MediaSubTypeNode(String mimeType) {
409  super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true), Lookups.singleton(mimeType));
410  this.mimeType = mimeType;
411  this.subType = StringUtils.substringAfter(mimeType, "/");
412  super.setName(mimeType);
413  super.setDisplayName(subType);
414  updateDisplayName();
415  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
416  addObserver(this);
417  }
418 
425  @Override
426  public boolean isLeafTypeNode() {
427  return true;
428  }
429 
430  @Override
431  public <T> T accept(DisplayableItemNodeVisitor< T> visitor) {
432  return visitor.visit(this);
433  }
434 
435  @Override
436  protected Sheet createSheet() {
437  Sheet sheet = super.createSheet();
438  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
439  if (sheetSet == null) {
440  sheetSet = Sheet.createPropertiesSet();
441  sheet.put(sheetSet);
442  }
443  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc"), getDisplayName()));
444  return sheet;
445  }
446 
447  @Override
448  public String getItemType() {
449  return getClass().getName();
450  }
451 
452  @Override
453  public void update(Observable o, Object arg) {
454  updateDisplayName();
455  }
456 
457  @Override
458  String getDisplayNameBase() {
459  return subType;
460  }
461 
462  @Override
463  long calculateChildCount() {
464  return existingMimeTypeCounts.get(StringUtils.substringBefore(mimeType, "/")).get(subType);
465  }
466  }
467 
473  private class MediaSubTypeNodeChildren extends BaseChildFactory<FileTypesKey> implements Observer {
474 
475  private final String mimeType;
476 
477  private MediaSubTypeNodeChildren(String mimeType) {
478  super(mimeType, new ViewsKnownAndSlackFilter<>());
479  addObserver(this);
480  this.mimeType = mimeType;
481  }
482 
483  @Override
484  public void update(Observable o, Object arg) {
485  refresh(true);
486  }
487 
488  @Override
489  protected Node createNodeForKey(FileTypesKey key) {
490  return key.accept(new FileTypes.FileNodeCreationVisitor());
491  }
492 
493  @Override
494  protected List<FileTypesKey> makeKeys() {
495  try {
497  .findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
498  .stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS
499  } catch (NoCurrentCaseException | TskCoreException ex) {
500  logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
501  }
502  return Collections.emptyList();
503  }
504 
505  @Override
506  protected void onAdd() {
507  // No-op
508  }
509 
510  @Override
511  protected void onRemove() {
512  // No-op
513  }
514  }
515 }
static synchronized IngestManager getInstance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
final HashMap< String, Map< String, Long > > existingMimeTypeCounts
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:756

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