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;
79 @NbBundle.Messages(
"AbstractAbstractFileNode.addFileProperty.desc=no description")
80 private static final String
NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
93 String ext = abstractFile.getNameExtension();
94 if (StringUtils.isNotBlank(ext)) {
110 translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
111 new ThreadFactoryBuilder().setNameFormat(
"translation-task-thread-%d").build());
140 enum NodeSpecificEvents {
141 TRANSLATION_AVAILABLE,
144 private final PropertyChangeListener
pcl = (PropertyChangeEvent evt) -> {
145 String eventType = evt.getPropertyName();
153 if ((moduleContentEvent.getSource() instanceof Content) ==
false) {
156 Content newContent = (Content) moduleContentEvent.getSource();
159 if (
getContent().getId() == newContent.getId()) {
162 Children parentsChildren = getParentNode().getChildren();
167 if (parentsChildren instanceof ContentChildren) {
168 ((ContentChildren) parentsChildren).refreshChildren();
169 parentsChildren.getNodesCount();
171 }
catch (NullPointerException ex) {
176 if (evt.getNewValue() == null) {
188 if (event.getAddedTag().getContent().equals(content)) {
189 List<ContentTag> tags = getContentTagsFromDatabase();
190 Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
191 Score value = scorePropAndDescr.getLeft();
192 String descr = scorePropAndDescr.getRight();
200 if (event.getDeletedTagInfo().getContentID() == content.getId()) {
201 List<ContentTag> tags = getContentTagsFromDatabase();
202 Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
203 Score value = scorePropAndDescr.getLeft();
204 String descr = scorePropAndDescr.getRight();
212 if (event.getContentID() == content.getId()) {
213 List<ContentTag> tags = getContentTagsFromDatabase();
217 }
else if (eventType.equals(NodeSpecificEvents.TRANSLATION_AVAILABLE.toString())) {
229 private final PropertyChangeListener
weakPcl = WeakListeners.propertyChange(pcl, null);
247 Sheet visibleSheet = this.getSheet();
248 Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
249 Property<?>[] visibleProps = visibleSheetSet.getProperties();
251 for(
int i = 0; i < visibleProps.length; i++) {
252 if(visibleProps[i].
getName().equals(newProp.getName())) {
253 visibleProps[i] = newProp;
257 visibleSheetSet.put(visibleProps);
258 visibleSheet.put(visibleSheetSet);
260 this.setSheet(visibleSheet);
271 Sheet sheet =
new Sheet();
272 Sheet.Set sheetSet = Sheet.createPropertiesSet();
277 newProperties.forEach((property) -> {
278 sheetSet.put(property);
285 translationPool.submit(
new TranslationTask(
new WeakReference<>(
this), weakPcl));
290 @NbBundle.Messages({
"AbstractAbstractFileNode.nameColLbl=Name",
291 "AbstractAbstractFileNode.translateFileName=Translated Name",
292 "AbstractAbstractFileNode.createSheet.score.name=S",
293 "AbstractAbstractFileNode.createSheet.comment.name=C",
294 "AbstractAbstractFileNode.createSheet.count.name=O",
295 "AbstractAbstractFileNode.locationColLbl=Location",
296 "AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time",
297 "AbstractAbstractFileNode.changeTimeColLbl=Change Time",
298 "AbstractAbstractFileNode.accessTimeColLbl=Access Time",
299 "AbstractAbstractFileNode.createdTimeColLbl=Created Time",
300 "AbstractAbstractFileNode.sizeColLbl=Size",
301 "AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir)",
302 "AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta)",
303 "AbstractAbstractFileNode.modeColLbl=Mode",
304 "AbstractAbstractFileNode.useridColLbl=UserID",
305 "AbstractAbstractFileNode.groupidColLbl=GroupID",
306 "AbstractAbstractFileNode.metaAddrColLbl=Meta Addr.",
307 "AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.",
308 "AbstractAbstractFileNode.typeDirColLbl=Type(Dir)",
309 "AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
310 "AbstractAbstractFileNode.knownColLbl=Known",
311 "AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
312 "AbstractAbstractFileNode.objectId=Object ID",
313 "AbstractAbstractFileNode.mimeType=MIME Type",
314 "AbstractAbstractFileNode.extensionColLbl=Extension"})
317 NAME(AbstractAbstractFileNode_nameColLbl()),
319 SCORE(AbstractAbstractFileNode_createSheet_score_name()),
320 COMMENT(AbstractAbstractFileNode_createSheet_comment_name()),
322 LOCATION(AbstractAbstractFileNode_locationColLbl()),
323 MOD_TIME(AbstractAbstractFileNode_modifiedTimeColLbl()),
327 SIZE(AbstractAbstractFileNode_sizeColLbl()),
330 MODE(AbstractAbstractFileNode_modeColLbl()),
331 USER_ID(AbstractAbstractFileNode_useridColLbl()),
332 GROUP_ID(AbstractAbstractFileNode_groupidColLbl()),
335 TYPE_DIR(AbstractAbstractFileNode_typeDirColLbl()),
337 KNOWN(AbstractAbstractFileNode_knownColLbl()),
338 MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
346 this.displayString = displayString;
351 return displayString;
359 List<NodeProperty<?>> properties =
new ArrayList<>();
360 properties.add(
new NodeProperty<>(NAME.toString(), NAME.toString(),
NO_DESCR, getContentDisplayName(content)));
371 List<ContentTag> tags = getContentTagsFromDatabase();
375 properties.add(
new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
379 Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
380 properties.add(
new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
382 properties.add(
new NodeProperty<>(LOCATION.toString(), LOCATION.toString(),
NO_DESCR, getContentPath(content)));
388 properties.add(
new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(),
NO_DESCR, content.getDirFlagAsString()));
389 properties.add(
new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(),
NO_DESCR, content.getMetaFlagsAsString()));
390 properties.add(
new NodeProperty<>(KNOWN.toString(), KNOWN.toString(),
NO_DESCR, content.getKnown().getName()));
391 properties.add(
new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(),
NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
392 properties.add(
new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(),
NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
393 properties.add(
new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(),
NO_DESCR, content.getNameExtension()));
407 @NbBundle.Messages(
"AbstractAbstractFileNode.tagsProperty.displayName=Tags")
410 List<ContentTag> tags = getContentTagsFromDatabase();
411 sheetSet.put(
new NodeProperty<>(
"Tags", AbstractAbstractFileNode_tagsProperty_displayName(),
412 NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName())
414 .collect(Collectors.joining(
", "))));
430 return StringUtils.join(file.getHashSetNames(),
", ");
431 }
catch (TskCoreException tskCoreException) {
432 logger.log(Level.WARNING,
"Error getting hashset hits: ", tskCoreException);
438 "AbstractAbstractFileNode.createSheet.count.displayName=O",
439 "AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
440 "AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
441 "# {0} - occuranceCount",
442 "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
443 Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
445 String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
448 if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
449 count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
450 description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
451 }
else if (attribute != null) {
452 description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
454 }
catch (EamDbException ex) {
455 logger.log(Level.WARNING,
"Error getting count of datasources with correlation attribute", ex);
456 }
catch (CorrelationAttributeNormalizationException ex) {
457 logger.log(Level.WARNING,
"Unable to normalize data to get count of datasources with correlation attribute", ex);
460 return Pair.of(count, description);
464 "AbstractAbstractFileNode.createSheet.score.displayName=S",
465 "AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.",
466 "AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.",
467 "AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
468 "AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
469 "AbstractAbstractFileNode.createSheet.noScore.description=No score"})
470 Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
471 DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
472 String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
473 if (content.getKnown() == TskData.FileKnown.BAD) {
474 score = DataResultViewerTable.Score.NOTABLE_SCORE;
475 description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description();
478 if (score == DataResultViewerTable.Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) {
479 score = DataResultViewerTable.Score.INTERESTING_SCORE;
480 description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description();
482 }
catch (TskCoreException ex) {
483 logger.log(Level.WARNING,
"Error getting artifacts for file: " + content.getName(), ex);
485 if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
486 score = DataResultViewerTable.Score.INTERESTING_SCORE;
487 description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
488 for (ContentTag tag : tags) {
489 if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
490 score = DataResultViewerTable.Score.NOTABLE_SCORE;
491 description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
496 return Pair.of(score, description);
500 "AbstractAbstractFileNode.createSheet.comment.displayName=C"})
501 HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
503 DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
505 for (ContentTag tag : tags) {
506 if (!StringUtils.isBlank(tag.getComment())) {
508 status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
512 if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
513 if (status == DataResultViewerTable.HasCommentStatus.TAG_COMMENT) {
514 status = DataResultViewerTable.HasCommentStatus.CR_AND_TAG_COMMENTS;
516 status = DataResultViewerTable.HasCommentStatus.CR_COMMENT;
526 String getTranslatedFileName() {
528 if (content.getName().matches(
"^\\p{ASCII}+$")) {
531 TextTranslationService tts = TextTranslationService.getInstance();
532 if (tts.hasProvider()) {
534 String base = FilenameUtils.getBaseName(content.getName());
536 String translation = tts.translate(base);
537 String ext = FilenameUtils.getExtension(content.getName());
540 String extensionDelimiter = (ext.isEmpty()) ?
"" :
".";
544 if (!translation.isEmpty()) {
545 return translation + extensionDelimiter + ext;
547 }
catch (NoServiceProviderException noServiceEx) {
548 logger.log(Level.WARNING,
"Translate unsuccessful because no TextTranslator "
549 +
"implementation was provided.", noServiceEx.getMessage());
550 }
catch (TranslationException noTranslationEx) {
551 logger.log(Level.WARNING,
"Could not successfully translate file name "
552 + content.getName(), noTranslationEx.getMessage());
563 List<ContentTag> getContentTagsFromDatabase() {
564 List<ContentTag> tags =
new ArrayList<>();
566 tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
567 }
catch (TskCoreException | NoCurrentCaseException ex) {
568 logger.log(Level.SEVERE,
"Failed to get tags for content " + content.getName(), ex);
573 CorrelationAttributeInstance getCorrelationAttributeInstance() {
574 CorrelationAttributeInstance attribute = null;
575 if (EamDbUtil.useCentralRepo() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
576 attribute = EamArtifactUtil.getInstanceFromContent(content);
581 static String getContentPath(AbstractFile file) {
583 return file.getUniquePath();
584 }
catch (TskCoreException ex) {
585 logger.log(Level.SEVERE,
"Except while calling Content.getUniquePath() on " + file.getName(), ex);
590 static String getContentDisplayName(AbstractFile file) {
591 String name = file.getName();
594 return DirectoryNode.DOTDOTDIR;
596 return DirectoryNode.DOTDIR;
613 map.put(NAME.toString(), getContentDisplayName(content));
614 map.put(LOCATION.toString(), getContentPath(content));
619 map.put(SIZE.toString(), content.getSize());
620 map.put(FLAGS_DIR.toString(), content.getDirFlagAsString());
621 map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
622 map.put(KNOWN.toString(), content.getKnown().getName());
623 map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
624 map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
625 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