1 /*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2018 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
92
93 @NbBundle.Messages("AccountsRootNode.name=Accounts")
94 final public static String
NAME = Bundle.AccountsRootNode_name();
95
98
99 /* Should rejected accounts be shown in the accounts section of the tree. */
101
104
112
115 }
116
117 @Override
119 return visitor.
visit(
this);
120 }
121
130 return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
131 }
132
142 }
143
151
157 super();
158 }
159
164 abstract protected boolean createKeys(List<X> list);
165
171 @Subscribe
173
174 @Subscribe
176
177 @Override
179 super.removeNotify();
181 }
182
183 @Override
185 super.addNotify();
186 refresh(true);
188 }
189 }
190
194 @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
196
200 setDisplayName(Bundle.Accounts_RootNode_displayName());
201 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
202 }
203
204 @Override
206 return false;
207 }
208
209 @Override
211 return visitor.
visit(
this);
212 }
213
214 @Override
216 return getClass().getName();
217 }
218 }
219
224
225 /*
226 * The pcl is in this class because it has the easiest mechanisms to add
227 * and remove itself during its life cycles.
228 */
229 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
230 @Override
231 public void propertyChange(PropertyChangeEvent evt) {
232 String eventType = evt.getPropertyName();
240 try {
249 if (null != eventData
251 reviewStatusBus.post(eventData);
252 }
254 // Case is closed, do nothing.
255 }
264 try {
266 refresh(true);
268 // Case is closed, do nothing.
269 }
271 // case was closed. Remove listeners so that we don't get called with a stale case handle
272 if (evt.getNewValue() == null) {
274 skCase = null;
275 }
276 }
277 }
278 };
279
280 @Subscribe
281 @Override
283 refresh(true);
284 }
285
286 @Subscribe
287 @Override
289 refresh(true);
290 }
291
292 @Override
294 try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(
295 "SELECT DISTINCT blackboard_attributes.value_text as account_type "
296 + " FROM blackboard_attributes "
297 + " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID());
298 ResultSet resultSet = executeQuery.getResultSet()) {
299 while (resultSet.next()) {
300 String accountType = resultSet.getString("account_type");
301 list.add(accountType);
302 }
303 } catch (TskCoreException | SQLException ex) {
304 LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
305 }
306
307 return true;
308 }
309
310 @Override
312
313 if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) {
315 } else {
316
317 try {
318 Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName);
320 } catch (TskCoreException ex) {
321 LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
322 }
323
324 return new Node[]{};
325 }
326 }
327
328 @Override
333 super.removeNotify();
334 }
335
336 @Override
341 super.addNotify();
342 refresh(true);
343 }
344
345 }
346
348
350
353 }
354
355 private final PropertyChangeListener
pcl =
new PropertyChangeListener() {
356 @Override
357 public void propertyChange(PropertyChangeEvent evt) {
358 String eventType = evt.getPropertyName();
366 try {
375 if (null != eventData
377 reviewStatusBus.post(eventData);
378 }
380 // Case is closed, do nothing.
381 }
390 try {
392 refresh(true);
393
395 // Case is closed, do nothing.
396 }
398 // case was closed. Remove listeners so that we don't get called with a stale case handle
399 if (evt.getNewValue() == null) {
401 skCase = null;
402 }
403 }
404 }
405 };
406
407 @Override
412 super.addNotify();
413 }
414
415 @Override
420 super.removeNotify();
421 }
422
423 @Override
425 String query =
426 "SELECT blackboard_artifacts.artifact_id " //NON-NLS
427 + " FROM blackboard_artifacts " //NON-NLS
428 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
429 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
430 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
431 +
" AND blackboard_attributes.value_text = '" +
accountType.getTypeName() +
"'" //NON-NLS
433 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
434 ResultSet rs = results.getResultSet();) {
435 while (rs.next()) {
436 list.add(rs.getLong("artifact_id")); //NON-NLS
437 }
438 } catch (TskCoreException | SQLException ex) {
439 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
440 }
441
442 return true;
443 }
444
445 @Override
447 try {
449 } catch (TskCoreException ex) {
450 LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
451 return new Node[0];
452 }
453 }
454
455 @Subscribe
456 @Override
458 refresh(true);
459 }
460
461 @Subscribe
462 @Override
464 refresh(true);
465 }
466 }
467
473
478 }
479
480 @Override
482 return true;
483 }
484
485 @Override
487 return visitor.
visit(
this);
488 }
489
490 @Override
492 return getClass().getName();
493 }
494 }
495
502 }
503
505
506 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
507 @Override
508 public void propertyChange(PropertyChangeEvent evt) {
509 String eventType = evt.getPropertyName();
517 try {
526 if (null != eventData
528 reviewStatusBus.post(eventData);
529 }
531 // Case is closed, do nothing.
532 }
541 try {
543 refresh(true);
544
546 // Case is closed, do nothing.
547 }
549 // case was closed. Remove listeners so that we don't get called with a stale case handle
550 if (evt.getNewValue() == null) {
552 skCase = null;
553 }
554 }
555 }
556 };
557
558 @Subscribe
559 @Override
561 refresh(true);
562 }
563
564 @Subscribe
565 @Override
567 refresh(true);
568 }
569
570 @Override
575 super.addNotify();
576 }
577
578 @Override
583 super.removeNotify();
584 }
585
589 @Override
590 protected boolean createKeys(List<CreditCardViewMode> list) {
592
593 return true;
594 }
595
596 @Override
598 switch (key) {
599 case BY_BIN:
601 case BY_FILE:
603 default:
604 return new Node[0];
605 }
606 }
607 }
608
613
619 super(Children.create(
new ViewModeFactory(),
true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
620 setName(Account.Type.CREDIT_CARD.getDisplayName());
621 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
622 }
623
624 @Override
626 return false;
627 }
628
629 @Override
631 return visitor.
visit(
this);
632 }
633
634 @Override
636 return getClass().getName();
637 }
638 }
639
641
642 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
643 @Override
644 public void propertyChange(PropertyChangeEvent evt) {
645 String eventType = evt.getPropertyName();
653 try {
662 if (null != eventData
664 reviewStatusBus.post(eventData);
665 }
667 // Case is closed, do nothing.
668 }
677 try {
679 refresh(true);
680
682 // Case is closed, do nothing.
683 }
685 // case was closed. Remove listeners so that we don't get called with a stale case handle
686 if (evt.getNewValue() == null) {
688 skCase = null;
689 }
690 }
691 }
692 };
693
694 @Override
699 super.addNotify();
700 }
701
702 @Override
707 super.removeNotify();
708 }
709
710 @Subscribe
711 @Override
713 refresh(true);
714 }
715
716 @Subscribe
717 @Override
719 refresh(true);
720 }
721
722 @Override
724 String query =
725 "SELECT blackboard_artifacts.obj_id," //NON-NLS
726 + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
727 if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
728 query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
729 + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
730 } else {
731 query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
732 + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
733 }
734 query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
735 + " FROM blackboard_artifacts " //NON-NLS
736 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
737 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
738 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
739 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
740 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
741 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
743 + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
744 + " ORDER BY hits DESC "; //NON-NLS
745 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
746 ResultSet resultSet = results.getResultSet();) {
747 while (resultSet.next()) {
749 resultSet.getLong("obj_id"), //NON-NLS
750 resultSet.getString("solr_document_id"), //NON-NLS
751 unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
752 resultSet.getLong("hits"), //NON-NLS
753 new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
754 }
755 } catch (TskCoreException | SQLException ex) {
756 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
757
758 }
759 return true;
760 }
761
762 @Override
764 //add all account artifacts for the file and the file itself to the lookup
765 try {
766 List<Object> lookupContents = new ArrayList<>();
768 lookupContents.add(skCase.getBlackboardArtifact(artId));
769 }
770 AbstractFile abstractFileById = skCase.getAbstractFileById(key.
getObjID());
771 lookupContents.add(abstractFileById);
772 return new Node[]{
new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
773 } catch (TskCoreException ex) {
774 LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
775 return new Node[0];
776 }
777 }
778 }
779
785
791 setName("By File"); //NON-NLS
792 updateDisplayName();
793 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
794 reviewStatusBus.register(this);
795 }
796
797 @NbBundle.Messages({
798 "# {0} - number of children",
799 "Accounts.ByFileNode.displayName=By File ({0})"})
801 String query =
802 "SELECT count(*) FROM ( SELECT count(*) AS documents "
803 + " FROM blackboard_artifacts " //NON-NLS
804 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
805 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
806 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
807 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
808 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
809 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
811 + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
812 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
813 ResultSet resultSet = results.getResultSet();) {
814 while (resultSet.next()) {
815 if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
816 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
817 } else {
818 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
819 }
820 }
821 } catch (TskCoreException | SQLException ex) {
822 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
823
824 }
825 }
826
827 @Override
829 return true;
830 }
831
832 @Override
834 return visitor.
visit(
this);
835 }
836
837 @Override
839 return getClass().getName();
840 }
841
842 @Subscribe
844 updateDisplayName();
845 }
846
847 @Subscribe
849 updateDisplayName();
850 }
851 }
852
854
855 private final PropertyChangeListener pcl =
new PropertyChangeListener() {
856 @Override
857 public void propertyChange(PropertyChangeEvent evt) {
858 String eventType = evt.getPropertyName();
866 try {
875 if (null != eventData
877 reviewStatusBus.post(eventData);
878 }
880 // Case is closed, do nothing.
881 }
890 try {
892
893 refresh(true);
895 // Case is closed, do nothing.
896 }
898 && (evt.getNewValue() == null)) {
899 // case was closed. Remove listeners so that we don't get called with a stale case handle
901 skCase = null;
902 }
903 }
904 };
905
906 @Override
911 super.addNotify();
912 }
913
914 @Override
919 super.removeNotify();
920 }
921
922 @Subscribe
923 @Override
925 refresh(true);
926 }
927
928 @Subscribe
929 @Override
931 refresh(true);
932 }
933
934 @Override
936
937 RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
938
939 String query =
940 "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
941 + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
942 + " FROM blackboard_artifacts " //NON-NLS
943 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
944 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
945 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
947 + " GROUP BY BIN " //NON-NLS
948 + " ORDER BY BIN "; //NON-NLS
949 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
950 ResultSet resultSet = results.getResultSet();) {
951 //sort all te individual bins in to the ranges
952 while (resultSet.next()) {
953 final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
954 long count = resultSet.getLong("count");
955
957 BinResult previousResult = binRanges.get(bin);
958
959 if (previousResult != null) {
960 binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
961 count += previousResult.getCount();
962 }
963
964 if (binRange == null) {
965 binRanges.put(Range.closed(bin, bin),
new BinResult(count, bin, bin));
966 } else {
968 }
969 }
970 binRanges.asMapOfRanges().values().forEach(list::add);
971 } catch (TskCoreException | SQLException ex) {
972 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
973 }
974
975 return true;
976 }
977
978 @Override
980 return new Node[]{
new BINNode(key)};
981 }
982 }
983
989
993 @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
995 super(Children.create(
new BINFactory(),
true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
996 setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
997 updateDisplayName();
998 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
999 reviewStatusBus.register(this);
1000 }
1001
1002 @NbBundle.Messages({
1003 "# {0} - number of children",
1004 "Accounts.ByBINNode.displayName=By BIN ({0})"})
1006 String query =
1007 "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1008 + " FROM blackboard_artifacts " //NON-NLS
1009 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1010 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1011 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1013 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1014 ResultSet resultSet = results.getResultSet();) {
1015 while (resultSet.next()) {
1016 setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1017 }
1018 } catch (TskCoreException | SQLException ex) {
1019 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1020 }
1021 }
1022
1023 @Override
1025 return false;
1026 }
1027
1028 @Override
1030 return visitor.
visit(
this);
1031 }
1032
1033 @Override
1035 return getClass().getName();
1036 }
1037
1038 @Subscribe
1040 updateDisplayName();
1041 }
1042
1043 @Subscribe
1045 updateDisplayName();
1046 }
1047 }
1048
1053 @Immutable
1055
1056 @Override
1058 int hash = 5;
1059 hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1060 hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1061 hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1062 hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1063 hash = 79 * hash + Objects.hashCode(this.statuses);
1064 return hash;
1065 }
1066
1067 @Override
1069 if (this == obj) {
1070 return true;
1071 }
1072 if (obj == null) {
1073 return false;
1074 }
1075 if (getClass() != obj.getClass()) {
1076 return false;
1077 }
1079 if (this.objID != other.
objID) {
1080 return false;
1081 }
1082 if (this.hits != other.
hits) {
1083 return false;
1084 }
1086 return false;
1087 }
1088 if (!Objects.equals(
this.artifactIDs, other.
artifactIDs)) {
1089 return false;
1090 }
1091 if (!Objects.equals(
this.statuses, other.
statuses)) {
1092 return false;
1093 }
1094 return true;
1095 }
1096
1101 private final Set<BlackboardArtifact.ReviewStatus>
statuses;
1102
1103 private FileWithCCN(
long objID, String solrDocID, List<Long> artifactIDs,
long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1104 this.objID = objID;
1105 this.keywordSearchDocID = solrDocID;
1106 this.artifactIDs = artifactIDs;
1107 this.hits = hits;
1108 this.statuses = statuses;
1109 }
1110
1117 return objID;
1118 }
1119
1127 return keywordSearchDocID;
1128 }
1129
1136 return artifactIDs;
1137 }
1138
1145 return hits;
1146 }
1147
1154 return statuses;
1155 }
1156 }
1157
1174 static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1175 return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1176 : Stream.of(groupConcat.split(",")) //NON-NLS
1177 .map(mapper::apply)
1178 .collect(Collectors.toList());
1179 }
1180
1185
1188
1198 @NbBundle.Messages({
1199 "# {0} - raw file name",
1200 "# {1} - solr chunk id",
1201 "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1203 super(Children.LEAF, Lookups.fixed(lookupContents));
1204 this.fileKey = key;
1206 ? content.getName()
1207 : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.
getkeywordSearchDocID(),
"_"));
//NON-NLS
1208 setName(fileName + key.
getObjID());
1209 setDisplayName(fileName);
1210 }
1211
1212 @Override
1214 return true;
1215 }
1216
1217 @Override
1219 return visitor.
visit(
this);
1220 }
1221
1222 @Override
1224 return getClass().getName();
1225 }
1226
1227 @Override
1228 @NbBundle.Messages({
1229 "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1230 "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1231 "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1232 "Accounts.FileWithCCNNode.noDescription=no description"})
1234 Sheet sheet = super.createSheet();
1235 Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1236 if (propSet == null) {
1237 propSet = Sheet.createPropertiesSet();
1238 sheet.put(propSet);
1239 }
1240
1241 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1242 Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1243 Bundle.Accounts_FileWithCCNNode_noDescription(),
1244 fileName));
1245 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1246 Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1247 Bundle.Accounts_FileWithCCNNode_noDescription(),
1249 propSet.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1250 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1251 Bundle.Accounts_FileWithCCNNode_noDescription(),
1253 .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1254 .collect(Collectors.joining(", ")))); //NON-NLS
1255
1256 return sheet;
1257 }
1258
1259 @Override
1261 Action[] actions = super.getActions(context);
1262 ArrayList<Action> arrayList = new ArrayList<>();
1263 arrayList.addAll(Arrays.asList(actions));
1264 try {
1266 } catch (TskCoreException ex) {
1267 LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1268 }
1269
1270 arrayList.add(approveActionInstance);
1271 arrayList.add(rejectActionInstance);
1272
1273 return arrayList.toArray(new Action[arrayList.size()]);
1274 }
1275 }
1276
1278
1280
1282 this.bin = bin;
1283 }
1284
1285 @Subscribe
1286 @Override
1288 refresh(true);
1289 }
1290
1291 @Subscribe
1292 @Override
1294 refresh(true);
1295 }
1296
1297 @Override
1299
1300 String query =
1301 "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1302 + " FROM blackboard_artifacts " //NON-NLS
1303 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1304 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1305 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1306 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1308 + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1309 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1310 ResultSet rs = results.getResultSet();) {
1311 while (rs.next()) {
1312 list.add(rs.getLong("artifact_id")); //NON-NLS
1313 }
1314 } catch (TskCoreException | SQLException ex) {
1315 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1316
1317 }
1318 return true;
1319 }
1320
1321 @Override
1323 if (skCase == null) {
1324 return new Node[0];
1325 }
1326
1327 try {
1328 BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1330 } catch (TskCoreException ex) {
1331 LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1332 return new Node[0];
1333 }
1334 }
1335 }
1336
1338 if (bin.getBINStart() == bin.getBINEnd()) {
1339 return Integer.toString(bin.getBINStart());
1340 } else {
1341 return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1342 }
1343 }
1344
1346
1349
1352 this.bin = bin;
1354 updateDisplayName();
1355 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1356 reviewStatusBus.register(this);
1357 }
1358
1359 @Subscribe
1361 updateDisplayName();
1362 updateSheet();
1363 }
1364
1365 @Subscribe
1367 updateDisplayName();
1368 }
1369
1371 String query =
1372 "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1373 + " FROM blackboard_artifacts " //NON-NLS
1374 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1375 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1376 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1377 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1379 try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1380 ResultSet resultSet = results.getResultSet();) {
1381 while (resultSet.next()) {
1382 setDisplayName(
getBinRangeString(bin) +
" (" + resultSet.getLong(
"count") +
")");
//NON-NLS
1383 }
1384 } catch (TskCoreException | SQLException ex) {
1385 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1386
1387 }
1388
1389 }
1390
1391 @Override
1393 return true;
1394 }
1395
1396 @Override
1398 return visitor.
visit(
this);
1399 }
1400
1401 @Override
1403 return getClass().getName();
1404 }
1405
1407 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1408 if (sheetSet == null) {
1409 sheetSet = Sheet.createPropertiesSet();
1410 sheet.put(sheetSet);
1411 }
1412 return sheetSet;
1413 }
1414
1415 @Override
1416 @NbBundle.Messages({
1417 "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1418 "Accounts.BINNode.accountsProperty.displayName=Accounts",
1419 "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1420 "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1421 "Accounts.BINNode.brandProperty.displayName=Brand",
1422 "Accounts.BINNode.bankProperty.displayName=Bank",
1423 "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1424 "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1425 "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1426 "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1427 "Accounts.BINNode.noDescription=no description"})
1429 Sheet sheet = super.createSheet();
1430 Sheet.Set properties = getPropertySet(sheet);
1431
1432 properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1433 Bundle.Accounts_BINNode_binProperty_displayName(),
1434 Bundle.Accounts_BINNode_noDescription(),
1436 properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1437 Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1438 bin.getCount()));
1439
1440 //add optional properties if they are available
1441 if (bin.hasDetails()) {
1442 bin.
getCardType().ifPresent(cardType -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1443 Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1444 cardType)));
1445 bin.
getScheme().ifPresent(scheme -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1446 Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1447 scheme)));
1448 bin.
getBrand().ifPresent(brand -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1449 Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1450 brand)));
1451 bin.
getBankName().ifPresent(bankName -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1452 Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1453 bankName)));
1454 bin.
getBankCity().ifPresent(bankCity -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1455 Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1456 bankCity)));
1457 bin.
getCountry().ifPresent(country -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1458 Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1459 country)));
1461 Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1462 phoneNumber)));
1463 bin.
getBankURL().ifPresent(url -> properties.put(
new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1464 Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1465 url)));
1466 }
1467 return sheet;
1468 }
1469
1471 this.setSheet(createSheet());
1472 }
1473
1474 }
1475
1480 @Immutable
1482
1483 @Override
1485 int hash = 3;
1486 hash = 97 * hash + this.binEnd;
1487 hash = 97 * hash + this.binStart;
1488 return hash;
1489 }
1490
1491 @Override
1493 if (this == obj) {
1494 return true;
1495 }
1496 if (obj == null) {
1497 return false;
1498 }
1499 if (getClass() != obj.getClass()) {
1500 return false;
1501 }
1503 if (this.binEnd != other.
binEnd) {
1504 return false;
1505 }
1506 if (this.binStart != other.
binStart) {
1507 return false;
1508 }
1509 return true;
1510 }
1511
1514
1518
1520 this.count = count;
1521 this.binRange = binRange;
1522 binStart = binRange.getBINstart();
1523 binEnd = binRange.getBINend();
1524 }
1525
1527 this.count = count;
1528 this.binRange = null;
1529 binStart = start;
1530 binEnd = end;
1531 }
1532
1533 int getBINStart() {
1534 return binStart;
1535 }
1536
1537 int getBINEnd() {
1538 return binEnd;
1539 }
1540
1541 long getCount() {
1542 return count;
1543 }
1544
1545 boolean hasDetails() {
1546 return binRange != null;
1547 }
1548
1549 @Override
1552 }
1553
1554 @Override
1557 }
1558
1559 @Override
1562 }
1563
1564 @Override
1567 }
1568
1569 @Override
1572 }
1573
1574 @Override
1577 }
1578
1579 @Override
1582 }
1583
1584 @Override
1587 }
1588
1589 @Override
1592 }
1593 }
1594
1596
1598
1600 super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1601 this.artifact = artifact;
1602 setName(Long.toString(this.artifact.getArtifactID()));
1603
1604 reviewStatusBus.register(this);
1605 }
1606
1607 @Override
1609 List<Action> actionsList = new ArrayList<>();
1610 actionsList.addAll(Arrays.asList(super.getActions(context)));
1611
1612 actionsList.add(approveActionInstance);
1613 actionsList.add(rejectActionInstance);
1614
1615 return actionsList.toArray(new Action[actionsList.size()]);
1616 }
1617
1618 @Override
1620 Sheet sheet = super.createSheet();
1621 Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1622 if (properties == null) {
1623 properties = Sheet.createPropertiesSet();
1624 sheet.put(properties);
1625 }
1626 properties.put(
new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1627 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1628 Bundle.Accounts_FileWithCCNNode_noDescription(),
1629 artifact.getReviewStatus().getDisplayName()));
1630
1631 return sheet;
1632 }
1633
1634 @Subscribe
1636
1637 // Update the node if event includes this artifact
1638 event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1639 return _item;
1640 }).forEachOrdered((_item) -> {
1641 updateSheet();
1642 });
1643 }
1644
1646 this.setSheet(createSheet());
1647 }
1648
1649 }
1650
1652
1653 @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1655 super(Bundle.ToggleShowRejected_name());
1656 }
1657
1658 @Override
1662 }
1663 }
1664
1666
1668
1670 super(displayName);
1671 this.newStatus = newStatus;
1672
1673 }
1674
1675 @Override
1677
1678 /* get paths for selected nodes to reselect after applying review
1679 * status change */
1680 List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1681 .map(node -> {
1682 String[] createPath;
1683 /*
1684 * If the we are rejecting and not showing rejected
1685 * results, then the selected node, won't exist any
1686 * more, so we select the previous one in stead.
1687 */
1688 if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1689 List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1690 if (siblings.size() > 1) {
1691 int indexOf = siblings.indexOf(node);
1692 //there is no previous for the first node, so instead we select the next one
1693 Node sibling = indexOf > 0
1694 ? siblings.get(indexOf - 1)
1695 : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1696 createPath = NodeOp.createPath(sibling, null);
1697 } else {
1698 /* if there are no other siblings to select,
1699 * just return null, but note we need to filter
1700 * this out of stream below */
1701 return null;
1702 }
1703 } else {
1704 createPath = NodeOp.createPath(node, null);
1705 }
1706 //for the reselect to work we need to strip off the first part of the path.
1707 return Arrays.copyOfRange(createPath, 1, createPath.length);
1708 })
1709 .filter(Objects::nonNull)
1710 .collect(Collectors.toList());
1711
1712 //change status of selected artifacts
1713 final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1714 artifacts.forEach(artifact -> {
1715 try {
1716 artifact.setReviewStatus(newStatus);
1717 } catch (TskCoreException ex) {
1718 LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1719 }
1720 });
1721 //post event
1722 reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1723
1724 final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1725 final Node rootNode = directoryListing.getRootNode();
1726
1727 //convert paths back to nodes
1728 List<Node> toArray = new ArrayList<>();
1729 selectedPaths.forEach(path -> {
1730 try {
1731 toArray.add(NodeOp.findPath(rootNode, path));
1732 } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1733 //just ingnore paths taht don't exist. this is expected since we are rejecting
1734 }
1735 });
1736 //select nodes
1737 directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1738 }
1739 }
1740
1742
1743 @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1745 super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1746 }
1747 }
1748
1750
1751 @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1753 super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1754 }
1755 }
1756
1758
1759 Collection<? extends BlackboardArtifact> artifacts;
1760 BlackboardArtifact.ReviewStatus newReviewStatus;
1761
1762 ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1763 this.artifacts = artifacts;
1764 this.newReviewStatus = newReviewStatus;
1765 }
1766 }
1767
1774
1775 if (type.equals(Account.Type.CREDIT_CARD)) {
1776 return ICON_BASE_PATH + "credit-card.png";
1777 } else if (type.equals(Account.Type.DEVICE)) {
1778 return ICON_BASE_PATH + "image.png";
1779 } else if (type.equals(Account.Type.EMAIL)) {
1780 return ICON_BASE_PATH + "email.png";
1781 } else if (type.equals(Account.Type.FACEBOOK)) {
1782 return ICON_BASE_PATH + "facebook.png";
1783 } else if (type.equals(Account.Type.INSTAGRAM)) {
1784 return ICON_BASE_PATH + "instagram.png";
1785 } else if (type.equals(Account.Type.MESSAGING_APP)) {
1786 return ICON_BASE_PATH + "messaging.png";
1787 } else if (type.equals(Account.Type.PHONE)) {
1788 return ICON_BASE_PATH + "phone.png";
1789 } else if (type.equals(Account.Type.TWITTER)) {
1790 return ICON_BASE_PATH + "twitter.png";
1791 } else if (type.equals(Account.Type.WEBSITE)) {
1792 return ICON_BASE_PATH + "web-file.png";
1793 } else if (type.equals(Account.Type.WHATSAPP)) {
1794 return ICON_BASE_PATH + "WhatsApp.png";
1795 } else {
1796 //there could be a default icon instead...
1797 throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
1798 }
1799 }
1800 }
CreditCardNumberFactory(BinResult bin)
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()
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()
static Case getOpenCase()
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
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)
final PropertyChangeListener pcl
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
final PropertyChangeListener pcl
boolean equals(Object obj)
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
static final String ICON_BASE_PATH
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
void actionPerformed(ActionEvent e)
Optional< String > getScheme()
Optional< String > getBankName()
Optional< String > getBankPhoneNumber()
Optional< String > getCardType()
Accounts(SleuthkitCase skCase)