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.accounts;
20
21 import com.google.common.collect.Range;
22 import com.google.common.collect.RangeMap;
23 import com.google.common.collect.TreeRangeMap;
24 import com.google.common.eventbus.EventBus;
25 import com.google.common.eventbus.Subscribe;
26 import java.awt.event.ActionEvent;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.EnumSet;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.Optional;
40 import java.util.Set;
41 import java.util.function.Function;
42 import java.util.logging.Level;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
45 import javax.annotation.Nonnull;
46 import javax.annotation.concurrent.Immutable;
47 import javax.swing.AbstractAction;
48 import javax.swing.Action;
49 import org.apache.commons.lang3.StringUtils;
50 import org.openide.nodes.ChildFactory;
51 import org.openide.nodes.Children;
52 import org.openide.nodes.Node;
53 import org.openide.nodes.NodeNotFoundException;
54 import org.openide.nodes.NodeOp;
55 import org.openide.nodes.Sheet;
56 import org.openide.util.NbBundle;
57 import org.openide.util.Utilities;
58 import org.openide.util.lookup.Lookups;
77 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
83
89
91 private static final String
ICON_BASE_PATH =
"/org/sleuthkit/autopsy/images/";
//NON-NLS
94
95 @NbBundle.Messages("AccountsRootNode.name=Accounts")
96 final public static String
NAME = Bundle.AccountsRootNode_name();
97
100
102
103 /*
104 * Should rejected accounts be shown in the accounts section of the tree.
105 */
107
110
118 }
119
126 public Accounts(SleuthkitCase skCase,
long objId) {
128 this.filteringDSObjId = objId;
129
132 }
133
134 @Override
136 return visitor.
visit(
this);
137 }
138
147 return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
148 }
149
157 if (filteringDSObjId > 0) {
158 return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
159 }
160
161 return " ";
162 }
163
171 @Deprecated
174 }
175
183
189 super();
190 }
191
196 @Override
197 abstract protected boolean createKeys(List<X> list);
198
204 @Subscribe
206
207 @Subscribe
209
210 @Override
212 super.removeNotify();
214 }
215
216 @Override
218 super.addNotify();
219 refresh(true);
221 }
222 }
223
227 @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
229
233 setDisplayName(Bundle.Accounts_RootNode_displayName());
234 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
235 }
236
237 @Override
239 return false;
240 }
241
242 @Override
244 return visitor.
visit(
this);
245 }
246
247 @Override
249 return getClass().getName();
250 }
251 }
252
257
258 /*
259 * The pcl is in this class because it has the easiest mechanisms to add
260 * and remove itself during its life cycles.
261 */
262 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
263 @Override
264 public void propertyChange(PropertyChangeEvent evt) {
265 String eventType = evt.getPropertyName();
273 try {
282 if (null != eventData
284 reviewStatusBus.post(eventData);
285 }
287 // Case is closed, do nothing.
288 }
297 try {
299 refresh(true);
301 // Case is closed, do nothing.
302 }
304 // case was closed. Remove listeners so that we don't get called with a stale case handle
305 if (evt.getNewValue() == null) {
307 skCase = null;
308 }
309 }
310 }
311 };
312
313 @Subscribe
314 @Override
316 refresh(true);
317 }
318
319 @Subscribe
320 @Override
322 refresh(true);
323 }
324
325 @Override
327 String accountTypesInUseQuery
328 = "SELECT DISTINCT blackboard_attributes.value_text as account_type "
329 + " FROM blackboard_artifacts " //NON-NLS
330 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
331 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
332 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
334
335 try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
336 ResultSet resultSet = executeQuery.getResultSet()) {
337 while (resultSet.next()) {
338 String accountType = resultSet.getString("account_type");
339 list.add(accountType);
340 }
341 } catch (TskCoreException | SQLException ex) {
342 LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
343 }
344
345 return true;
346 }
347
348 @Override
350
351 if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) {
353 } else {
354
355 try {
356 Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName);
358 } catch (TskCoreException ex) {
359 LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
360 }
361
362 return new Node[]{};
363 }
364 }
365
366 @Override
371 super.removeNotify();
372 }
373
374 @Override
379 super.addNotify();
380 refresh(true);
381 }
382
383 }
384
386
388
391 }
392
393 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
394 @Override
395 public void propertyChange(PropertyChangeEvent evt) {
396 String eventType = evt.getPropertyName();
404 try {
413 if (null != eventData
415 reviewStatusBus.post(eventData);
416 }
418 // Case is closed, do nothing.
419 }
428 try {
430 refresh(true);
431
433 // Case is closed, do nothing.
434 }
436 // case was closed. Remove listeners so that we don't get called with a stale case handle
437 if (evt.getNewValue() == null) {
439 skCase = null;
440 }
441 }
442 }
443 };
444
445 @Override
450 super.addNotify();
451 }
452
453 @Override
458 super.removeNotify();
459 }
460
461 @Override
463 String query
464 = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
465 + " FROM blackboard_artifacts " //NON-NLS
466 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
467 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
468 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
469 +
" AND blackboard_attributes.value_text = '" +
accountType.getTypeName() +
"'" //NON-NLS
472 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
473 ResultSet rs = results.getResultSet();) {
474 while (rs.next()) {
475 list.add(rs.getLong("artifact_id")); //NON-NLS
476 }
477 } catch (TskCoreException | SQLException ex) {
478 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
479 }
480
481 return true;
482 }
483
484 @Override
486 try {
488 } catch (TskCoreException ex) {
489 LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
490 return new Node[0];
491 }
492 }
493
494 @Subscribe
495 @Override
497 refresh(true);
498 }
499
500 @Subscribe
501 @Override
503 refresh(true);
504 }
505 }
506
512
517 }
518
519 @Override
521 return true;
522 }
523
524 @Override
526 return visitor.
visit(
this);
527 }
528
529 @Override
531 return getClass().getName();
532 }
533 }
534
541 }
542
544
545 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
546 @Override
547 public void propertyChange(PropertyChangeEvent evt) {
548 String eventType = evt.getPropertyName();
556 try {
565 if (null != eventData
567 reviewStatusBus.post(eventData);
568 }
570 // Case is closed, do nothing.
571 }
580 try {
582 refresh(true);
583
585 // Case is closed, do nothing.
586 }
588 // case was closed. Remove listeners so that we don't get called with a stale case handle
589 if (evt.getNewValue() == null) {
591 skCase = null;
592 }
593 }
594 }
595 };
596
597 @Subscribe
598 @Override
600 refresh(true);
601 }
602
603 @Subscribe
604 @Override
606 refresh(true);
607 }
608
609 @Override
614 super.addNotify();
615 }
616
617 @Override
622 super.removeNotify();
623 }
624
628 @Override
629 protected boolean createKeys(List<CreditCardViewMode> list) {
631
632 return true;
633 }
634
635 @Override
637 switch (key) {
638 case BY_BIN:
640 case BY_FILE:
642 default:
643 return new Node[0];
644 }
645 }
646 }
647
652
658 super(Children.create(
new ViewModeFactory(),
true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
659 setName(Account.Type.CREDIT_CARD.getDisplayName());
660 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
661 }
662
663 @Override
665 return false;
666 }
667
668 @Override
670 return visitor.
visit(
this);
671 }
672
673 @Override
675 return getClass().getName();
676 }
677 }
678
680
681 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
682 @Override
683 public void propertyChange(PropertyChangeEvent evt) {
684 String eventType = evt.getPropertyName();
692 try {
701 if (null != eventData
703 reviewStatusBus.post(eventData);
704 }
706 // Case is closed, do nothing.
707 }
716 try {
718 refresh(true);
719
721 // Case is closed, do nothing.
722 }
724 // case was closed. Remove listeners so that we don't get called with a stale case handle
725 if (evt.getNewValue() == null) {
727 skCase = null;
728 }
729 }
730 }
731 };
732
733 @Override
738 super.addNotify();
739 }
740
741 @Override
746 super.removeNotify();
747 }
748
749 @Subscribe
750 @Override
752 refresh(true);
753 }
754
755 @Subscribe
756 @Override
758 refresh(true);
759 }
760
761 @Override
763 String query
764 = "SELECT blackboard_artifacts.obj_id," //NON-NLS
765 + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
766 if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
767 query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
768 + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
769 } else {
770 query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
771 + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
772 }
773 query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
774 + " FROM blackboard_artifacts " //NON-NLS
775 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
776 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
777 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
778 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
779 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
780 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
783 + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
784 + " ORDER BY hits DESC "; //NON-NLS
785 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
786 ResultSet resultSet = results.getResultSet();) {
787 while (resultSet.next()) {
789 resultSet.getLong("obj_id"), //NON-NLS
790 resultSet.getString("solr_document_id"), //NON-NLS
791 unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
792 resultSet.getLong("hits"), //NON-NLS
793 new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
794 }
795 } catch (TskCoreException | SQLException ex) {
796 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
797
798 }
799 return true;
800 }
801
802 @Override
804 //add all account artifacts for the file and the file itself to the lookup
805 try {
806 List<Object> lookupContents = new ArrayList<>();
808 lookupContents.add(skCase.getBlackboardArtifact(artId));
809 }
810 AbstractFile abstractFileById = skCase.getAbstractFileById(key.
getObjID());
811 lookupContents.add(abstractFileById);
812 return new Node[]{
new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
813 } catch (TskCoreException ex) {
814 LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
815 return new Node[0];
816 }
817 }
818 }
819
825
831 setName("By File"); //NON-NLS
832 updateDisplayName();
833 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
834 reviewStatusBus.register(this);
835 }
836
837 @NbBundle.Messages({
838 "# {0} - number of children",
839 "Accounts.ByFileNode.displayName=By File ({0})"})
841 String query
842 = "SELECT count(*) FROM ( SELECT count(*) AS documents "
843 + " FROM blackboard_artifacts " //NON-NLS
844 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
845 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
846 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
847 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
848 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
849 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
852 + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
853 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
854 ResultSet resultSet = results.getResultSet();) {
855 while (resultSet.next()) {
856 if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
857 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
858 } else {
859 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
860 }
861 }
862 } catch (TskCoreException | SQLException ex) {
863 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
864
865 }
866 }
867
868 @Override
870 return true;
871 }
872
873 @Override
875 return visitor.
visit(
this);
876 }
877
878 @Override
880 return getClass().getName();
881 }
882
883 @Subscribe
885 updateDisplayName();
886 }
887
888 @Subscribe
890 updateDisplayName();
891 }
892 }
893
895
896 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
897 @Override
898 public void propertyChange(PropertyChangeEvent evt) {
899 String eventType = evt.getPropertyName();
907 try {
916 if (null != eventData
918 reviewStatusBus.post(eventData);
919 }
921 // Case is closed, do nothing.
922 }
931 try {
933
934 refresh(true);
936 // Case is closed, do nothing.
937 }
939 && (evt.getNewValue() == null)) {
940 // case was closed. Remove listeners so that we don't get called with a stale case handle
942 skCase = null;
943 }
944 }
945 };
946
947 @Override
952 super.addNotify();
953 }
954
955 @Override
960 super.removeNotify();
961 }
962
963 @Subscribe
964 @Override
966 refresh(true);
967 }
968
969 @Subscribe
970 @Override
972 refresh(true);
973 }
974
975 @Override
977
978 RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
979
980 String query
981 = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
982 + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
983 + " FROM blackboard_artifacts " //NON-NLS
984 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
985 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
986 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
989 + " GROUP BY BIN " //NON-NLS
990 + " ORDER BY BIN "; //NON-NLS
991 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
992 ResultSet resultSet = results.getResultSet();) {
993 //sort all te individual bins in to the ranges
994 while (resultSet.next()) {
995 final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
996 long count = resultSet.getLong("count");
997
999 BinResult previousResult = binRanges.get(bin);
1000
1001 if (previousResult != null) {
1002 binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
1003 count += previousResult.getCount();
1004 }
1005
1006 if (binRange == null) {
1007 binRanges.put(Range.closed(bin, bin),
new BinResult(count, bin, bin));
1008 } else {
1010 }
1011 }
1012 binRanges.asMapOfRanges().values().forEach(list::add);
1013 } catch (TskCoreException | SQLException ex) {
1014 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1015 }
1016
1017 return true;
1018 }
1019
1020 @Override
1022 return new Node[]{
new BINNode(key)};
1023 }
1024 }
1025
1031
1035 @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
1037 super(Children.create(
new BINFactory(),
true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
1038 setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
1039 updateDisplayName();
1040 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1041 reviewStatusBus.register(this);
1042 }
1043
1044 @NbBundle.Messages({
1045 "# {0} - number of children",
1046 "Accounts.ByBINNode.displayName=By BIN ({0})"})
1048 String query
1049 = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1050 + " FROM blackboard_artifacts " //NON-NLS
1051 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1052 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1053 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1056 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1057 ResultSet resultSet = results.getResultSet();) {
1058 while (resultSet.next()) {
1059 setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1060 }
1061 } catch (TskCoreException | SQLException ex) {
1062 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1063 }
1064 }
1065
1066 @Override
1068 return false;
1069 }
1070
1071 @Override
1073 return visitor.
visit(
this);
1074 }
1075
1076 @Override
1078 return getClass().getName();
1079 }
1080
1081 @Subscribe
1083 updateDisplayName();
1084 }
1085
1086 @Subscribe
1088 updateDisplayName();
1089 }
1090 }
1091
1096 @Immutable
1098
1099 @Override
1101 int hash = 5;
1102 hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1103 hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1104 hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1105 hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1106 hash = 79 * hash + Objects.hashCode(this.statuses);
1107 return hash;
1108 }
1109
1110 @Override
1112 if (this == obj) {
1113 return true;
1114 }
1115 if (obj == null) {
1116 return false;
1117 }
1118 if (getClass() != obj.getClass()) {
1119 return false;
1120 }
1122 if (this.objID != other.
objID) {
1123 return false;
1124 }
1125 if (this.hits != other.
hits) {
1126 return false;
1127 }
1129 return false;
1130 }
1131 if (!Objects.equals(
this.artifactIDs, other.
artifactIDs)) {
1132 return false;
1133 }
1134 if (!Objects.equals(
this.statuses, other.
statuses)) {
1135 return false;
1136 }
1137 return true;
1138 }
1139
1144 private final Set<BlackboardArtifact.ReviewStatus>
statuses;
1145
1146 private FileWithCCN(
long objID, String solrDocID, List<Long> artifactIDs,
long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1147 this.objID = objID;
1148 this.keywordSearchDocID = solrDocID;
1149 this.artifactIDs = artifactIDs;
1150 this.hits = hits;
1151 this.statuses = statuses;
1152 }
1153
1160 return objID;
1161 }
1162
1170 return keywordSearchDocID;
1171 }
1172
1179 return Collections.unmodifiableList(artifactIDs);
1180 }
1181
1188 return hits;
1189 }
1190
1197 return Collections.unmodifiableSet(statuses);
1198 }
1199 }
1200
1217 static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1218 return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1219 : Stream.of(groupConcat.split(",")) //NON-NLS
1220 .map(mapper::apply)
1221 .collect(Collectors.toList());
1222 }
1223
1228
1231
1241 @NbBundle.Messages({
1242 "# {0} - raw file name",
1243 "# {1} - solr chunk id",
1244 "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1246 super(Children.LEAF, Lookups.fixed(lookupContents));
1247 this.fileKey = key;
1249 ? content.getName()
1250 : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.
getkeywordSearchDocID(),
"_"));
//NON-NLS
1251 setName(fileName + key.
getObjID());
1252 setDisplayName(fileName);
1253 }
1254
1255 @Override
1257 return true;
1258 }
1259
1260 @Override
1262 return visitor.
visit(
this);
1263 }
1264
1265 @Override
1267 return getClass().getName();
1268 }
1269
1270 @Override
1271 @NbBundle.Messages({
1272 "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1273 "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1274 "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1275 "Accounts.FileWithCCNNode.noDescription=no description"})
1277 Sheet sheet = super.createSheet();
1278 Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1279 if (propSet == null) {
1280 propSet = Sheet.createPropertiesSet();
1281 sheet.put(propSet);
1282 }
1283
1284 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1285 Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1286 Bundle.Accounts_FileWithCCNNode_noDescription(),
1287 fileName));
1288 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1289 Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1290 Bundle.Accounts_FileWithCCNNode_noDescription(),
1292 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1293 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1294 Bundle.Accounts_FileWithCCNNode_noDescription(),
1296 .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1297 .collect(Collectors.joining(", ")))); //NON-NLS
1298
1299 return sheet;
1300 }
1301
1302 @Override
1304 Action[] actions = super.getActions(context);
1305 ArrayList<Action> arrayList = new ArrayList<>();
1306 arrayList.addAll(Arrays.asList(actions));
1307 try {
1309 } catch (TskCoreException ex) {
1310 LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1311 }
1312
1313 arrayList.add(approveActionInstance);
1314 arrayList.add(rejectActionInstance);
1315
1316 return arrayList.toArray(new Action[arrayList.size()]);
1317 }
1318 }
1319
1321
1323
1325 this.bin = bin;
1326 }
1327
1328 @Subscribe
1329 @Override
1331 refresh(true);
1332 }
1333
1334 @Subscribe
1335 @Override
1337 refresh(true);
1338 }
1339
1340 @Override
1342
1343 String query
1344 = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1345 + " FROM blackboard_artifacts " //NON-NLS
1346 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1347 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1348 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1349 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1352 + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1353 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1354 ResultSet rs = results.getResultSet();) {
1355 while (rs.next()) {
1356 list.add(rs.getLong("artifact_id")); //NON-NLS
1357 }
1358 } catch (TskCoreException | SQLException ex) {
1359 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1360
1361 }
1362 return true;
1363 }
1364
1365 @Override
1367 if (skCase == null) {
1368 return new Node[0];
1369 }
1370
1371 try {
1372 BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1374 } catch (TskCoreException ex) {
1375 LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1376 return new Node[0];
1377 }
1378 }
1379 }
1380
1382 if (bin.getBINStart() == bin.getBINEnd()) {
1383 return Integer.toString(bin.getBINStart());
1384 } else {
1385 return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1386 }
1387 }
1388
1390
1395
1398 this.bin = bin;
1400 updateDisplayName();
1401 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1402 reviewStatusBus.register(this);
1403 }
1404
1405 @Subscribe
1407 updateDisplayName();
1408 updateSheet();
1409 }
1410
1411 @Subscribe
1413 updateDisplayName();
1414 }
1415
1417 String query
1418 = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1419 + " FROM blackboard_artifacts " //NON-NLS
1420 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1421 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1422 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1423 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1426 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1427 ResultSet resultSet = results.getResultSet();) {
1428 while (resultSet.next()) {
1429 setDisplayName(
getBinRangeString(bin) +
" (" + resultSet.getLong(
"count") +
")");
//NON-NLS
1430 }
1431 } catch (TskCoreException | SQLException ex) {
1432 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1433
1434 }
1435
1436 }
1437
1438 @Override
1440 return true;
1441 }
1442
1443 @Override
1445 return visitor.
visit(
this);
1446 }
1447
1448 @Override
1450 return getClass().getName();
1451 }
1452
1454 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1455 if (sheetSet == null) {
1456 sheetSet = Sheet.createPropertiesSet();
1457 sheet.put(sheetSet);
1458 }
1459 return sheetSet;
1460 }
1461
1462 @Override
1463 @NbBundle.Messages({
1464 "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1465 "Accounts.BINNode.accountsProperty.displayName=Accounts",
1466 "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1467 "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1468 "Accounts.BINNode.brandProperty.displayName=Brand",
1469 "Accounts.BINNode.bankProperty.displayName=Bank",
1470 "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1471 "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1472 "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1473 "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1474 "Accounts.BINNode.noDescription=no description"})
1476 Sheet sheet = super.createSheet();
1477 Sheet.Set properties = getPropertySet(sheet);
1478
1479 properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1480 Bundle.Accounts_BINNode_binProperty_displayName(),
1481 Bundle.Accounts_BINNode_noDescription(),
1483 properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1484 Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1485 bin.getCount()));
1486
1487 //add optional properties if they are available
1488 if (bin.hasDetails()) {
1489 bin.
getCardType().ifPresent(cardType -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1490 Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1491 cardType)));
1492 bin.
getScheme().ifPresent(scheme -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1493 Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1494 scheme)));
1495 bin.
getBrand().ifPresent(brand -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1496 Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1497 brand)));
1498 bin.
getBankName().ifPresent(bankName -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1499 Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1500 bankName)));
1501 bin.
getBankCity().ifPresent(bankCity -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1502 Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1503 bankCity)));
1504 bin.
getCountry().ifPresent(country -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1505 Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1506 country)));
1508 Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1509 phoneNumber)));
1510 bin.
getBankURL().ifPresent(url -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1511 Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1512 url)));
1513 }
1514 return sheet;
1515 }
1516
1518 this.setSheet(createSheet());
1519 }
1520
1521 }
1522
1527 @Immutable
1529
1530 @Override
1532 int hash = 3;
1533 hash = 97 * hash + this.binEnd;
1534 hash = 97 * hash + this.binStart;
1535 return hash;
1536 }
1537
1538 @Override
1540 if (this == obj) {
1541 return true;
1542 }
1543 if (obj == null) {
1544 return false;
1545 }
1546 if (getClass() != obj.getClass()) {
1547 return false;
1548 }
1550 if (this.binEnd != other.
binEnd) {
1551 return false;
1552 }
1553 if (this.binStart != other.
binStart) {
1554 return false;
1555 }
1556 return true;
1557 }
1558
1563
1567
1569 this.count = count;
1570 this.binRange = binRange;
1571 binStart = binRange.getBINstart();
1572 binEnd = binRange.getBINend();
1573 }
1574
1576 this.count = count;
1577 this.binRange = null;
1578 binStart = start;
1579 binEnd = end;
1580 }
1581
1582 int getBINStart() {
1583 return binStart;
1584 }
1585
1586 int getBINEnd() {
1587 return binEnd;
1588 }
1589
1590 long getCount() {
1591 return count;
1592 }
1593
1594 boolean hasDetails() {
1595 return binRange != null;
1596 }
1597
1598 @Override
1601 }
1602
1603 @Override
1606 }
1607
1608 @Override
1611 }
1612
1613 @Override
1616 }
1617
1618 @Override
1621 }
1622
1623 @Override
1626 }
1627
1628 @Override
1631 }
1632
1633 @Override
1636 }
1637
1638 @Override
1641 }
1642 }
1643
1645
1647
1649 super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1650 this.artifact = artifact;
1651 setName(Long.toString(this.artifact.getArtifactID()));
1652
1653 reviewStatusBus.register(this);
1654 }
1655
1656 @Override
1658 List<Action> actionsList = new ArrayList<>();
1659 actionsList.addAll(Arrays.asList(super.getActions(context)));
1660
1661 actionsList.add(approveActionInstance);
1662 actionsList.add(rejectActionInstance);
1663
1664 return actionsList.toArray(new Action[actionsList.size()]);
1665 }
1666
1667 @Override
1669 Sheet sheet = super.createSheet();
1670 Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1671 if (properties == null) {
1672 properties = Sheet.createPropertiesSet();
1673 sheet.put(properties);
1674 }
1675 properties.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1676 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1677 Bundle.Accounts_FileWithCCNNode_noDescription(),
1678 artifact.getReviewStatus().getDisplayName()));
1679
1680 return sheet;
1681 }
1682
1683 @Subscribe
1685
1686 // Update the node if event includes this artifact
1687 event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1688 return _item;
1689 }).forEachOrdered((_item) -> {
1690 updateSheet();
1691 });
1692 }
1693
1695 this.setSheet(createSheet());
1696 }
1697
1698 }
1699
1700 @Deprecated
1702
1703 @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1705 super(Bundle.ToggleShowRejected_name());
1706 }
1707
1708 @Override
1712 }
1713 }
1714
1723 }
1724
1726
1728
1730 super(displayName);
1731 this.newStatus = newStatus;
1732
1733 }
1734
1735 @Override
1737
1738 /*
1739 * get paths for selected nodes to reselect after applying review
1740 * status change
1741 */
1742 List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1743 .map(node -> {
1744 String[] createPath;
1745 /*
1746 * If the we are rejecting and not showing rejected
1747 * results, then the selected node, won't exist any
1748 * more, so we select the previous one in stead.
1749 */
1750 if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1751 List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1752 if (siblings.size() > 1) {
1753 int indexOf = siblings.indexOf(node);
1754 //there is no previous for the first node, so instead we select the next one
1755 Node sibling = indexOf > 0
1756 ? siblings.get(indexOf - 1)
1757 : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1758 createPath = NodeOp.createPath(sibling, null);
1759 } else {
1760 /*
1761 * if there are no other siblings to select,
1762 * just return null, but note we need to filter
1763 * this out of stream below
1764 */
1765 return null;
1766 }
1767 } else {
1768 createPath = NodeOp.createPath(node, null);
1769 }
1770 //for the reselect to work we need to strip off the first part of the path.
1771 return Arrays.copyOfRange(createPath, 1, createPath.length);
1772 })
1773 .filter(Objects::nonNull)
1774 .collect(Collectors.toList());
1775
1776 //change status of selected artifacts
1777 final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1778 artifacts.forEach(artifact -> {
1779 try {
1780 artifact.setReviewStatus(newStatus);
1781 } catch (TskCoreException ex) {
1782 LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1783 }
1784 });
1785 //post event
1786 reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1787
1788 final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1789 final Node rootNode = directoryListing.getRootNode();
1790
1791 //convert paths back to nodes
1792 List<Node> toArray = new ArrayList<>();
1793 selectedPaths.forEach(path -> {
1794 try {
1795 toArray.add(NodeOp.findPath(rootNode, path));
1796 } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1797 //just ingnore paths taht don't exist. this is expected since we are rejecting
1798 }
1799 });
1800 //select nodes
1801 directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1802 }
1803 }
1804
1806
1807 @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1809 super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1810 }
1811 }
1812
1814
1815 @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1817 super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1818 }
1819 }
1820
1822
1823 Collection<? extends BlackboardArtifact> artifacts;
1824 BlackboardArtifact.ReviewStatus newReviewStatus;
1825
1826 ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1827 this.artifacts = artifacts;
1828 this.newReviewStatus = newReviewStatus;
1829 }
1830 }
1831
1838
1839 if (type.equals(Account.Type.CREDIT_CARD)) {
1840 return ICON_BASE_PATH + "credit-card.png";
1841 } else if (type.equals(Account.Type.DEVICE)) {
1842 return ICON_BASE_PATH + "image.png";
1843 } else if (type.equals(Account.Type.EMAIL)) {
1844 return ICON_BASE_PATH + "email.png";
1845 } else if (type.equals(Account.Type.FACEBOOK)) {
1846 return ICON_BASE_PATH + "facebook.png";
1847 } else if (type.equals(Account.Type.INSTAGRAM)) {
1848 return ICON_BASE_PATH + "instagram.png";
1849 } else if (type.equals(Account.Type.MESSAGING_APP)) {
1850 return ICON_BASE_PATH + "messaging.png";
1851 } else if (type.equals(Account.Type.PHONE)) {
1852 return ICON_BASE_PATH + "phone.png";
1853 } else if (type.equals(Account.Type.TWITTER)) {
1854 return ICON_BASE_PATH + "twitter.png";
1855 } else if (type.equals(Account.Type.WEBSITE)) {
1856 return ICON_BASE_PATH + "web-file.png";
1857 } else if (type.equals(Account.Type.WHATSAPP)) {
1858 return ICON_BASE_PATH + "WhatsApp.png";
1859 } else {
1860 //there could be a default icon instead...
1861 return ICON_BASE_PATH + "face.png";
1862 // throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
1863 }
1864 }
1865 }
CreditCardNumberFactory(BinResult bin)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
final BlackboardArtifact.ReviewStatus newStatus
boolean createKeys(List< CreditCardViewMode > list)
DefaultAccountFactory(Account.Type accountType)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
Optional< Integer > getNumberLength()
Node[] createNodesForKey(Long t)
Optional< String > getCountry()
final BlackboardArtifact artifact
static synchronized IngestManager getInstance()
String getBinRangeString(BinResult bin)
static final Logger LOGGER
Node[] createNodesForKey(BinResult key)
Optional< String > getBankPhoneNumber()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
void setShowRejected(boolean showRejected)
Optional< String > getCountry()
Sheet.Set getPropertySet(Sheet sheet)
Optional< String > getBankCity()
final String keywordSearchDocID
static List< Action > getActions(File file, boolean isArtifactSource)
Optional< String > getBankName()
final EventBus reviewStatusBus
String getRejectedArtifactFilterClause()
final RejectAccounts rejectActionInstance
List< Long > getArtifactIDs()
boolean createKeys(List< String > list)
abstract boolean createKeys(List< X > list)
AccountArtifactNode(BlackboardArtifact artifact)
static synchronized BankIdentificationNumber getBINInfo(int bin)
Node[] createNodesForKey(Long artifactID)
Action[] getActions(boolean context)
Node[] createNodesForKey(FileWithCCN key)
static String getIconFilePath(Account.Type type)
boolean createKeys(List< FileWithCCN > list)
final Account.Type accountType
Optional< String > getBankURL()
boolean createKeys(List< Long > list)
final ApproveAccounts approveActionInstance
final long filteringDSObjId
T visit(DataSourcesNode in)
Node[] createNodesForKey(CreditCardViewMode key)
void removeIngestJobEventListener(final PropertyChangeListener listener)
Optional< String > getScheme()
final Set< BlackboardArtifact.ReviewStatus > statuses
Optional< String > getBrand()
boolean equals(Object obj)
CreditCardNumberAccountTypeNode()
void addIngestJobEventListener(final PropertyChangeListener listener)
DefaultAccountTypeNode(Account.Type accountType)
Optional< String > getBankURL()
String getkeywordSearchDocID()
Optional< Integer > getNumberLength()
BinResult(long count,@Nonnull BINRange binRange)
Action newToggleShowRejectedAction()
Optional< String > getBrand()
Node[] createNodesForKey(String acountTypeName)
boolean createKeys(List< Long > list)
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Action[] getActions(boolean context)
final List< Long > artifactIDs
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
void actionPerformed(ActionEvent e)
boolean createKeys(List< BinResult > list)
Optional< String > getBankCity()
Optional< String > getCardType()
final FileWithCCN fileKey
BinResult(long count, int start, int end)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Accounts(SleuthkitCase skCase, long objId)
static Case getCurrentCaseThrows()
final PropertyChangeListener pcl
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
final PropertyChangeListener pcl
boolean equals(Object obj)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
static final String ICON_BASE_PATH
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
void actionPerformed(ActionEvent e)
String getFilterByDataSourceClause()
Optional< String > getScheme()
Optional< String > getBankName()
Optional< String > getBankPhoneNumber()
Optional< String > getCardType()
Accounts(SleuthkitCase skCase)