19 package org.sleuthkit.autopsy.datamodel;
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.lang.ref.WeakReference;
25 import java.util.ArrayList;
26 import java.util.EnumSet;
27 import java.util.List;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.logging.Level;
33 import java.util.stream.Collectors;
34 import org.apache.commons.io.FilenameUtils;
35 import org.apache.commons.lang3.StringUtils;
36 import org.apache.commons.lang3.tuple.Pair;
37 import org.openide.nodes.Children;
38 import org.openide.nodes.Sheet;
39 import org.openide.util.NbBundle;
40 import org.openide.util.WeakListeners;
78 @NbBundle.Messages(
"AbstractAbstractFileNode.addFileProperty.desc=no description")
79 private static final String
NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
92 String ext = abstractFile.getNameExtension();
93 if (StringUtils.isNotBlank(ext)) {
104 new WeakReference<>(
this),
weakPcl));
115 translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
116 new ThreadFactoryBuilder().setNameFormat(
"translation-task-thread-%d").build());
145 enum NodeSpecificEvents {
146 TRANSLATION_AVAILABLE,
149 private final PropertyChangeListener
pcl = (PropertyChangeEvent evt) -> {
150 String eventType = evt.getPropertyName();
158 if ((moduleContentEvent.getSource() instanceof Content) ==
false) {
161 Content newContent = (Content) moduleContentEvent.getSource();
164 if (
getContent().getId() == newContent.getId()) {
167 Children parentsChildren = getParentNode().getChildren();
172 if (parentsChildren instanceof ContentChildren) {
173 ((ContentChildren) parentsChildren).refreshChildren();
174 parentsChildren.getNodesCount();
176 }
catch (NullPointerException ex) {
181 if (evt.getNewValue() == null) {
193 if (event.getAddedTag().getContent().equals(content)) {
194 List<ContentTag> tags = getContentTagsFromDatabase();
195 Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
196 Score value = scorePropAndDescr.getLeft();
197 String descr = scorePropAndDescr.getRight();
205 if (event.getDeletedTagInfo().getContentID() == content.getId()) {
206 List<ContentTag> tags = getContentTagsFromDatabase();
207 Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
208 Score value = scorePropAndDescr.getLeft();
209 String descr = scorePropAndDescr.getRight();
217 if (event.getContentID() == content.getId()) {
218 List<ContentTag> tags = getContentTagsFromDatabase();
222 }
else if (eventType.equals(NodeSpecificEvents.TRANSLATION_AVAILABLE.toString())) {
223 this.setDisplayName(evt.getNewValue().toString());
225 this.setShortDescription(content.getName());
237 private final PropertyChangeListener
weakPcl = WeakListeners.propertyChange(pcl, null);
255 Sheet visibleSheet = this.getSheet();
256 Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
257 Property<?>[] visibleProps = visibleSheetSet.getProperties();
259 for (
int i = 0; i < visibleProps.length; i++) {
260 if (visibleProps[i].
getName().equals(newProp.getName())) {
261 visibleProps[i] = newProp;
265 visibleSheetSet.put(visibleProps);
266 visibleSheet.put(visibleSheetSet);
268 this.setSheet(visibleSheet);
279 Sheet sheet =
new Sheet();
280 Sheet.Set sheetSet = Sheet.createPropertiesSet();
285 newProperties.forEach((property) -> {
286 sheetSet.put(property);
292 @NbBundle.Messages({
"AbstractAbstractFileNode.nameColLbl=Name",
293 "AbstractAbstractFileNode.originalName=Original Name",
294 "AbstractAbstractFileNode.createSheet.score.name=S",
295 "AbstractAbstractFileNode.createSheet.comment.name=C",
296 "AbstractAbstractFileNode.createSheet.count.name=O",
297 "AbstractAbstractFileNode.locationColLbl=Location",
298 "AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time",
299 "AbstractAbstractFileNode.changeTimeColLbl=Change Time",
300 "AbstractAbstractFileNode.accessTimeColLbl=Access Time",
301 "AbstractAbstractFileNode.createdTimeColLbl=Created Time",
302 "AbstractAbstractFileNode.sizeColLbl=Size",
303 "AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir)",
304 "AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta)",
305 "AbstractAbstractFileNode.modeColLbl=Mode",
306 "AbstractAbstractFileNode.useridColLbl=UserID",
307 "AbstractAbstractFileNode.groupidColLbl=GroupID",
308 "AbstractAbstractFileNode.metaAddrColLbl=Meta Addr.",
309 "AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.",
310 "AbstractAbstractFileNode.typeDirColLbl=Type(Dir)",
311 "AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
312 "AbstractAbstractFileNode.knownColLbl=Known",
313 "AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
314 "AbstractAbstractFileNode.objectId=Object ID",
315 "AbstractAbstractFileNode.mimeType=MIME Type",
316 "AbstractAbstractFileNode.extensionColLbl=Extension"})
319 NAME(AbstractAbstractFileNode_nameColLbl()),
321 SCORE(AbstractAbstractFileNode_createSheet_score_name()),
322 COMMENT(AbstractAbstractFileNode_createSheet_comment_name()),
324 LOCATION(AbstractAbstractFileNode_locationColLbl()),
325 MOD_TIME(AbstractAbstractFileNode_modifiedTimeColLbl()),
329 SIZE(AbstractAbstractFileNode_sizeColLbl()),
332 MODE(AbstractAbstractFileNode_modeColLbl()),
333 USER_ID(AbstractAbstractFileNode_useridColLbl()),
334 GROUP_ID(AbstractAbstractFileNode_groupidColLbl()),
337 TYPE_DIR(AbstractAbstractFileNode_typeDirColLbl()),
339 KNOWN(AbstractAbstractFileNode_knownColLbl()),
340 MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
348 this.displayString = displayString;
353 return displayString;
361 List<NodeProperty<?>> properties =
new ArrayList<>();
362 properties.add(
new NodeProperty<>(NAME.toString(), NAME.toString(),
NO_DESCR, getContentDisplayName(content)));
373 List<ContentTag> tags = getContentTagsFromDatabase();
377 properties.add(
new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
381 Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
382 properties.add(
new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
384 properties.add(
new NodeProperty<>(LOCATION.toString(), LOCATION.toString(),
NO_DESCR, getContentPath(content)));
390 properties.add(
new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(),
NO_DESCR, content.getDirFlagAsString()));
391 properties.add(
new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(),
NO_DESCR, content.getMetaFlagsAsString()));
392 properties.add(
new NodeProperty<>(KNOWN.toString(), KNOWN.toString(),
NO_DESCR, content.getKnown().getName()));
393 properties.add(
new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(),
NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
394 properties.add(
new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(),
NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
395 properties.add(
new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(),
NO_DESCR, content.getNameExtension()));
409 @NbBundle.Messages(
"AbstractAbstractFileNode.tagsProperty.displayName=Tags")
412 List<ContentTag> tags = getContentTagsFromDatabase();
413 sheetSet.put(
new NodeProperty<>(
"Tags", AbstractAbstractFileNode_tagsProperty_displayName(),
414 NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName())
416 .collect(Collectors.joining(
", "))));
432 return StringUtils.join(file.getHashSetNames(),
", ");
433 }
catch (TskCoreException tskCoreException) {
434 logger.log(Level.WARNING,
"Error getting hashset hits: ", tskCoreException);
440 "AbstractAbstractFileNode.createSheet.count.displayName=O",
441 "AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
442 "AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
443 "# {0} - occuranceCount",
444 "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
445 Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
447 String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
450 if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
451 count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
452 description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
453 }
else if (attribute != null) {
454 description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
456 }
catch (EamDbException ex) {
457 logger.log(Level.WARNING,
"Error getting count of datasources with correlation attribute", ex);
458 }
catch (CorrelationAttributeNormalizationException ex) {
459 logger.log(Level.WARNING,
"Unable to normalize data to get count of datasources with correlation attribute", ex);
462 return Pair.of(count, description);
466 "AbstractAbstractFileNode.createSheet.score.displayName=S",
467 "AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.",
468 "AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.",
469 "AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
470 "AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
471 "AbstractAbstractFileNode.createSheet.noScore.description=No score"})
472 Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
473 DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
474 String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
475 if (content.getKnown() == TskData.FileKnown.BAD) {
476 score = DataResultViewerTable.Score.NOTABLE_SCORE;
477 description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description();
480 if (score == DataResultViewerTable.Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) {
481 score = DataResultViewerTable.Score.INTERESTING_SCORE;
482 description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description();
484 }
catch (TskCoreException ex) {
485 logger.log(Level.WARNING,
"Error getting artifacts for file: " + content.getName(), ex);
487 if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
488 score = DataResultViewerTable.Score.INTERESTING_SCORE;
489 description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
490 for (ContentTag tag : tags) {
491 if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
492 score = DataResultViewerTable.Score.NOTABLE_SCORE;
493 description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
498 return Pair.of(score, description);
502 "AbstractAbstractFileNode.createSheet.comment.displayName=C"})
503 HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
505 DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
507 for (ContentTag tag : tags) {
508 if (!StringUtils.isBlank(tag.getComment())) {
510 status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
514 if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
515 if (status == DataResultViewerTable.HasCommentStatus.TAG_COMMENT) {
516 status = DataResultViewerTable.HasCommentStatus.CR_AND_TAG_COMMENTS;
518 status = DataResultViewerTable.HasCommentStatus.CR_COMMENT;
528 String getTranslatedFileName() {
530 if (content.getName().matches(
"^\\p{ASCII}+$")) {
533 TextTranslationService tts = TextTranslationService.getInstance();
534 if (tts.hasProvider()) {
536 String base = FilenameUtils.getBaseName(content.getName());
538 String translation = tts.translate(base);
539 String ext = FilenameUtils.getExtension(content.getName());
542 String extensionDelimiter = (ext.isEmpty()) ?
"" :
".";
546 if (!translation.isEmpty()) {
547 return translation + extensionDelimiter + ext;
549 }
catch (NoServiceProviderException noServiceEx) {
550 logger.log(Level.WARNING,
"Translate unsuccessful because no TextTranslator "
551 +
"implementation was provided.", noServiceEx.getMessage());
552 }
catch (TranslationException noTranslationEx) {
553 logger.log(Level.WARNING,
"Could not successfully translate file name "
554 + content.getName(), noTranslationEx.getMessage());
565 List<ContentTag> getContentTagsFromDatabase() {
566 List<ContentTag> tags =
new ArrayList<>();
568 tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
569 }
catch (TskCoreException | NoCurrentCaseException ex) {
570 logger.log(Level.SEVERE,
"Failed to get tags for content " + content.getName(), ex);
575 CorrelationAttributeInstance getCorrelationAttributeInstance() {
576 CorrelationAttributeInstance attribute = null;
577 if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
578 attribute = EamArtifactUtil.getInstanceFromContent(content);
583 static String getContentPath(AbstractFile file) {
585 return file.getUniquePath();
586 }
catch (TskCoreException ex) {
587 logger.log(Level.SEVERE,
"Except while calling Content.getUniquePath() on " + file.getName(), ex);
592 static String getContentDisplayName(AbstractFile file) {
593 String name = file.getName();
596 return DirectoryNode.DOTDOTDIR;
598 return DirectoryNode.DOTDIR;
615 map.put(NAME.toString(), getContentDisplayName(content));
616 map.put(LOCATION.toString(), getContentPath(content));
621 map.put(SIZE.toString(), content.getSize());
622 map.put(FLAGS_DIR.toString(), content.getDirFlagAsString());
623 map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
624 map.put(KNOWN.toString(), content.getKnown().getName());
625 map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
626 map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
627 map.put(EXTENSION.toString(), content.getNameExtension());
static final Logger logger
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static String getStringTime(long epochSeconds, TimeZone tzone)
static List< String > getArchiveExtensions()
final String displayString
static synchronized IngestManager getInstance()
synchronized Sheet createSheet()
static void fillPropertyMap(Map< String, Object > map, AbstractFile content)
static final Set< Case.Events > CASE_EVENTS_OF_INTEREST
static boolean displayTranslatedFileNames()
static final ExecutorService translationPool
synchronized void updateSheet(NodeProperty<?>...newProps)
static final String NO_DESCR
static final Integer MAX_POOL_SIZE
AbstractFilePropertyType(String displayString)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static boolean hideCentralRepoCommentsAndOccurrences()
final PropertyChangeListener pcl
List< NodeProperty<?> > getProperties()
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static String getHashSetHitsCsvList(AbstractFile file)
void addTagProperty(Sheet.Set sheetSet)
final PropertyChangeListener weakPcl