Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
Accounts.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2021 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 */
19package org.sleuthkit.autopsy.datamodel.accounts;
20
21import com.google.common.collect.Range;
22import com.google.common.collect.RangeMap;
23import com.google.common.collect.TreeRangeMap;
24import com.google.common.eventbus.EventBus;
25import com.google.common.eventbus.Subscribe;
26import java.awt.event.ActionEvent;
27import java.beans.PropertyChangeEvent;
28import java.beans.PropertyChangeListener;
29import java.sql.ResultSet;
30import java.sql.SQLException;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Collection;
34import java.util.Collections;
35import java.util.EnumSet;
36import java.util.HashMap;
37import java.util.HashSet;
38import java.util.List;
39import java.util.Map;
40import java.util.Objects;
41import java.util.Optional;
42import java.util.Set;
43import java.util.function.Function;
44import java.util.logging.Level;
45import java.util.stream.Collectors;
46import java.util.stream.Stream;
47import javax.annotation.Nonnull;
48import javax.annotation.concurrent.Immutable;
49import javax.swing.AbstractAction;
50import javax.swing.Action;
51import javax.swing.SwingUtilities;
52import org.apache.commons.lang3.StringUtils;
53import org.openide.nodes.ChildFactory;
54import org.openide.nodes.Children;
55import org.openide.nodes.Node;
56import org.openide.nodes.NodeNotFoundException;
57import org.openide.nodes.NodeOp;
58import org.openide.nodes.Sheet;
59import org.openide.util.NbBundle;
60import org.openide.util.Utilities;
61import org.openide.util.WeakListeners;
62import org.openide.util.lookup.Lookups;
63import org.sleuthkit.autopsy.casemodule.Case;
64import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
65import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
66import org.sleuthkit.autopsy.coreutils.Logger;
67import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor;
68import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem;
69import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
70import org.sleuthkit.autopsy.datamodel.CreditCards;
71import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
72import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
73import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
74import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
75import org.sleuthkit.autopsy.datamodel.NodeProperty;
76import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
77import org.sleuthkit.autopsy.ingest.IngestManager;
78import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
79import org.sleuthkit.datamodel.AbstractFile;
80import org.sleuthkit.datamodel.Account;
81import org.sleuthkit.datamodel.BlackboardArtifact;
82import org.sleuthkit.datamodel.BlackboardArtifact.Type;
83import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
84import org.sleuthkit.datamodel.BlackboardAttribute;
85import org.sleuthkit.datamodel.Content;
86import org.sleuthkit.datamodel.DataArtifact;
87import org.sleuthkit.datamodel.SleuthkitCase;
88import org.sleuthkit.datamodel.TskCoreException;
89import org.sleuthkit.datamodel.TskData.DbType;
90
95final public class Accounts implements AutopsyVisitableItem {
96
97 private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
98 private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
101 private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName();
102
103 @NbBundle.Messages("AccountsRootNode.name=Accounts") //used for the viewArtifact navigation
104 final public static String NAME = Bundle.AccountsRootNode_name();
105
106 private final long filteringDSObjId; // 0 if not filtering/grouping by data source
107
108 private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
109
110 /*
111 * Should rejected accounts be shown in the accounts section of the tree.
112 */
113 private boolean showRejected = false; //NOPMD redundant initializer
114
117
118 // tracks the number of each account type found
120
126 public Accounts() {
127 this(0);
128 }
129
136 public Accounts(long objId) {
137 this.filteringDSObjId = objId;
138
139 this.rejectActionInstance = new RejectAccounts();
140 this.approveActionInstance = new ApproveAccounts();
141 this.accountTypeResults = new AccountTypeResults();
142 }
143
144 @Override
145 public <T> T accept(AutopsyItemVisitor<T> visitor) {
146 return visitor.visit(this);
147 }
148
157 return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
158 }
159
167 if (filteringDSObjId > 0) {
168 return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
169 }
170
171 return " ";
172 }
173
181 @Deprecated
183 return new ToggleShowRejected();
184 }
185
192 private abstract class ObservingChildren<X> extends ChildFactory.Detachable<X> {
193
198 ObservingChildren() {
199 super();
200 }
201
206 @Override
207 abstract protected boolean createKeys(List<X> list);
208
214 @Subscribe
215 abstract void handleReviewStatusChange(ReviewStatusChangeEvent event);
216
217 @Subscribe
218 abstract void handleDataAdded(ModuleDataEvent event);
219
220 @Override
221 protected void finalize() throws Throwable {
222 super.finalize();
223 reviewStatusBus.unregister(ObservingChildren.this);
224 }
225
226 @Override
227 protected void addNotify() {
228 super.addNotify();
229 refresh(true);
230 reviewStatusBus.register(ObservingChildren.this);
231 }
232 }
233
237 @NbBundle.Messages({"Accounts.RootNode.displayName=Communication Accounts"})
238 final public class AccountsRootNode extends UpdatableCountTypeNode {
239
241 super(Children.create(new AccountTypeFactory(), true),
242 Lookups.singleton(Accounts.this),
245 TSK_ACCOUNT);
246
247 setName(Accounts.NAME);
248 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
249 }
250
251 @Override
252 public boolean isLeafTypeNode() {
253 return false;
254 }
255
256 @Override
257 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
258 return visitor.visit(this);
259 }
260
261 @Override
262 public String getItemType() {
263 return getClass().getName();
264 }
265
266 @Override
267 protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
268 String accountTypesInUseQuery
269 = "SELECT COUNT(*) AS count\n"
270 + "FROM (\n"
271 + " SELECT MIN(blackboard_attributes.value_text) AS account_type\n"
272 + " FROM blackboard_artifacts\n"
273 + " LEFT JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id\n"
274 + " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() + "\n"
275 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.Type.TSK_ACCOUNT_TYPE.getTypeID() + "\n"
276 + " AND blackboard_attributes.value_text IS NOT NULL\n"
278 + " -- group by artifact_id to ensure only one account type per artifact\n"
279 + " GROUP BY blackboard_artifacts.artifact_id\n"
280 + ") res\n";
281
282 try (SleuthkitCase.CaseDbQuery executeQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(accountTypesInUseQuery);
283 ResultSet resultSet = executeQuery.getResultSet()) {
284
285 if (resultSet.next()) {
286 return resultSet.getLong("count");
287 }
288
289 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
290 LOGGER.log(Level.SEVERE, "Error querying for count of all account types", ex);
291 }
292
293 return 0;
294 }
295
296 }
297
301 private class AccountTypeResults {
302
303 private final Map<String, Long> counts = new HashMap<>();
304
305 AccountTypeResults() {
306 update();
307 }
308
317 Long getCount(String accountType) {
318 return counts.get(accountType);
319 }
320
326 List<String> getTypes() {
327 List<String> types = new ArrayList<>(counts.keySet());
328 Collections.sort(types);
329 return types;
330 }
331
335 private void update() {
336 String accountTypesInUseQuery
337 = "SELECT res.account_type, COUNT(*) AS count\n"
338 + "FROM (\n"
339 + " SELECT MIN(blackboard_attributes.value_text) AS account_type\n"
340 + " FROM blackboard_artifacts\n"
341 + " LEFT JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id\n"
342 + " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() + "\n"
343 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.Type.TSK_ACCOUNT_TYPE.getTypeID() + "\n"
345 + " -- group by artifact_id to ensure only one account type per artifact\n"
346 + " GROUP BY blackboard_artifacts.artifact_id\n"
347 + ") res\n"
348 + "GROUP BY res.account_type";
349
350 try (SleuthkitCase.CaseDbQuery executeQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(accountTypesInUseQuery);
351 ResultSet resultSet = executeQuery.getResultSet()) {
352
353 counts.clear();
354 while (resultSet.next()) {
355 String accountType = resultSet.getString("account_type");
356 Long count = resultSet.getLong("count");
357 counts.put(accountType, count);
358 }
359 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
360 LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
361 }
362 }
363 }
364
368 private class AccountTypeFactory extends ObservingChildren<String> {
369
370 /*
371 * The pcl is in this class because it has the easiest mechanisms to add
372 * and remove itself during its life cycles.
373 */
374 private final PropertyChangeListener pcl = new PropertyChangeListener() {
375 @Override
376 public void propertyChange(PropertyChangeEvent evt) {
377 String eventType = evt.getPropertyName();
378 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
385 try {
393 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
394 if (null != eventData
395 && eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
396 accountTypeResults.update();
397 reviewStatusBus.post(eventData);
398 }
399 } catch (NoCurrentCaseException notUsed) {
400 // Case is closed, do nothing.
401 }
402 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
403 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
410 try {
412 accountTypeResults.update();
413 refresh(true);
414 } catch (NoCurrentCaseException notUsed) {
415 // Case is closed, do nothing.
416 }
417 }
418 }
419 };
420
421 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
422
423 @Subscribe
424 @Override
425 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
426 refresh(true);
427 }
428
429 @Subscribe
430 @Override
431 void handleDataAdded(ModuleDataEvent event) {
432 refresh(true);
433 }
434
435 @Override
436 protected boolean createKeys(List<String> list) {
437 list.addAll(accountTypeResults.getTypes());
438 return true;
439 }
440
449 private Node[] getNodeArr(Node node) {
450 reviewStatusBus.register(node);
451 return new Node[]{node};
452 }
453
454 @Override
455 protected Node[] createNodesForKey(String accountTypeName) {
456
457 if (Account.Type.CREDIT_CARD.getTypeName().equals(accountTypeName)) {
459 } else {
460
461 try {
462 Account.Type accountType = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().getAccountType(accountTypeName);
463 if (accountType != null) {
464 return getNodeArr(new DefaultAccountTypeNode(accountType));
465 } else {
466 // This can only happen if a TSK_ACCOUNT artifact was created not using CommunicationManager
467 LOGGER.log(Level.SEVERE, "Unknown account type '" + accountTypeName + "' found - account will not be displayed.\n"
468 + "Account type names must match an entry in the display_name column of the account_types table.\n"
469 + "Accounts should be created using the CommunicationManager API.");
470 }
471 } catch (TskCoreException | NoCurrentCaseException ex) {
472 LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
473 }
474
475 return new Node[]{};
476 }
477 }
478
479 @Override
486
487 @Override
495
496 }
497
498 final private class DefaultAccountFactory extends ObservingChildren<Long> {
499
500 private final Account.Type accountType;
501
502 private DefaultAccountFactory(Account.Type accountType) {
503 this.accountType = accountType;
504 }
505
506 private final PropertyChangeListener pcl = new PropertyChangeListener() {
507 @Override
508 public void propertyChange(PropertyChangeEvent evt) {
509 String eventType = evt.getPropertyName();
510 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
517 try {
525 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
526 if (null != eventData
527 && eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
528 reviewStatusBus.post(eventData);
529 }
530 } catch (NoCurrentCaseException notUsed) {
531 // Case is closed, do nothing.
532 }
533 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
534 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
541 try {
543 refresh(true);
544
545 } catch (NoCurrentCaseException notUsed) {
546 // Case is closed, do nothing.
547 }
548 }
549 }
550 };
551
552 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
553
554 @Override
560
561 @Override
568
569 @Override
570 protected boolean createKeys(List<Long> list) {
571 String query
572 = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS
573 + " FROM blackboard_artifacts " //NON-NLS
574 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
575 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
576 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
577 + " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
580 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
581 ResultSet rs = results.getResultSet();) {
582 List<Long> tempList = new ArrayList<>();
583 while (rs.next()) {
584 tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS
585 }
586 list.addAll(tempList);
587 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
588 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
589 }
590
591 return true;
592 }
593
594 @Override
595 protected Node[] createNodesForKey(Long t) {
596 try {
597 return new Node[]{new BlackboardArtifactNode(Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getDataArtifactById(t))};
598 } catch (TskCoreException | NoCurrentCaseException ex) {
599 LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
600 return new Node[0];
601 }
602 }
603
604 @Subscribe
605 @Override
606 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
607 refresh(true);
608 }
609
610 @Subscribe
611 @Override
612 void handleDataAdded(ModuleDataEvent event) {
613 refresh(true);
614 }
615 }
616
621 final public class DefaultAccountTypeNode extends DisplayableItemNode {
622
623 private final Account.Type accountType;
624
625 private DefaultAccountTypeNode(Account.Type accountType) {
626 super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
627 this.accountType = accountType;
628 String iconPath = getIconFilePath(accountType);
629 this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
630 setName(accountType.getTypeName());
631 updateName();
632 }
633
634 @Override
635 public boolean isLeafTypeNode() {
636 return true;
637 }
638
639 @Override
640 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
641 return visitor.visit(this);
642 }
643
644 @Override
645 public String getItemType() {
646 return getClass().getName();
647 }
648
649 @Subscribe
650 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
651 updateName();
652 }
653
654 @Subscribe
655 void handleDataAdded(ModuleDataEvent event) {
656 updateName();
657 }
658
663 public void updateName() {
664 setDisplayName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
665 }
666 }
667
675
676 final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
677
678 private final PropertyChangeListener pcl = new PropertyChangeListener() {
679 @Override
680 public void propertyChange(PropertyChangeEvent evt) {
681 String eventType = evt.getPropertyName();
682 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
689 try {
697 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
698 if (null != eventData
699 && eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
700 reviewStatusBus.post(eventData);
701 }
702 } catch (NoCurrentCaseException notUsed) {
703 // Case is closed, do nothing.
704 }
705 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
706 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
713 try {
715 refresh(true);
716
717 } catch (NoCurrentCaseException notUsed) {
718 // Case is closed, do nothing.
719 }
720 }
721 }
722 };
723
724 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
725
726 @Subscribe
727 @Override
728 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
729 refresh(true);
730 }
731
732 @Subscribe
733 @Override
734 void handleDataAdded(ModuleDataEvent event) {
735 refresh(true);
736 }
737
738 @Override
745
746 @Override
747 protected void finalize() throws Throwable {
748 super.finalize();
752 super.removeNotify();
753 }
754
758 @Override
759 protected boolean createKeys(List<CreditCardViewMode> list) {
760 list.addAll(Arrays.asList(CreditCardViewMode.values()));
761
762 return true;
763 }
764
765 @Override
766 protected Node[] createNodesForKey(CreditCardViewMode key) {
767 switch (key) {
768 case BY_BIN:
769 return new Node[]{new ByBINNode()};
770 case BY_FILE:
771 return new Node[]{new ByFileNode()};
772 default:
773 return new Node[0];
774 }
775 }
776 }
777
782
788 super(Children.create(new ViewModeFactory(), true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
789 setName(Account.Type.CREDIT_CARD.getDisplayName());
790 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
791 }
792
797 public void updateName() {
798 setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
799 }
800
801 @Subscribe
802 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
803 updateName();
804 }
805
806 @Subscribe
807 void handleDataAdded(ModuleDataEvent event) {
808 updateName();
809 }
810
811 @Override
812 public boolean isLeafTypeNode() {
813 return false;
814 }
815
816 @Override
817 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
818 return visitor.visit(this);
819 }
820
821 @Override
822 public String getItemType() {
823 return getClass().getName();
824 }
825 }
826
827 final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
828
829 private final PropertyChangeListener pcl = new PropertyChangeListener() {
830 @Override
831 public void propertyChange(PropertyChangeEvent evt) {
832 String eventType = evt.getPropertyName();
833 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
840 try {
848 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
849 if (null != eventData
850 && eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
851 reviewStatusBus.post(eventData);
852 }
853 } catch (NoCurrentCaseException notUsed) {
854 // Case is closed, do nothing.
855 }
856 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
857 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
864 try {
866 refresh(true);
867
868 } catch (NoCurrentCaseException notUsed) {
869 // Case is closed, do nothing.
870 }
871 }
872 }
873 };
874
875 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
876
877 @Override
884
885 @Override
892
893 @Subscribe
894 @Override
895 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
896 refresh(true);
897 }
898
899 @Subscribe
900 @Override
901 void handleDataAdded(ModuleDataEvent event) {
902 refresh(true);
903 }
904
905 @Override
906 protected boolean createKeys(List<FileWithCCN> list) {
907 try {
908 String query
909 = "SELECT blackboard_artifacts.obj_id," //NON-NLS
910 + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
911 if (Case.getCurrentCaseThrows().getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) {
912 query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
913 + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
914 } else {
915 query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
916 + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
917 }
918 query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
919 + " FROM blackboard_artifacts " //NON-NLS
920 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
921 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
922 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
923 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
924 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
925 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
928 + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
929 + " ORDER BY hits DESC "; //NON-NLS
930 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
931 ResultSet resultSet = results.getResultSet();) {
932 while (resultSet.next()) {
933 long file_id = resultSet.getLong("obj_id");
934 AbstractFile abstractFileById = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(file_id);
935 if (abstractFileById != null) {
936 list.add(new FileWithCCN(
937 abstractFileById,
938 file_id, //NON-NLS
939 resultSet.getString("solr_document_id"), //NON-NLS
940 unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
941 resultSet.getLong("hits"), //NON-NLS
942 new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
943 }
944 }
945 } catch (TskCoreException | SQLException ex) {
946 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
947 }
948
949 } catch (NoCurrentCaseException ex) {
950 LOGGER.log(Level.SEVERE, "Error getting case.", ex);
951 }
952 return true;
953 }
954
955 @Override
956 protected Node[] createNodesForKey(FileWithCCN key) {
957 //add all account artifacts for the file and the file itself to the lookup
958 try {
959 List<Object> lookupContents = new ArrayList<>();
960 for (long artId : key.artifactIDs) {
961 lookupContents.add(Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(artId));
962 }
963 AbstractFile abstractFileById = key.getFile();
964 lookupContents.add(abstractFileById);
965 return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
966 } catch (TskCoreException | NoCurrentCaseException ex) {
967 LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
968 return new Node[0];
969 }
970 }
971 }
972
977 final public class ByFileNode extends DisplayableItemNode {
978
982 private ByFileNode() {
983 super(Children.create(new FileWithCCNFactory(), true), Lookups.singleton("By File"));
984 setName("By File"); //NON-NLS
986 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
987 reviewStatusBus.register(this);
988 }
989
990 @NbBundle.Messages({
991 "# {0} - number of children",
992 "Accounts.ByFileNode.displayName=By File ({0})"})
993 private void updateDisplayName() {
994 String query
995 = "SELECT count(*) FROM ( SELECT count(*) AS documents "
996 + " FROM blackboard_artifacts " //NON-NLS
997 + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
998 + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
999 + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
1000 + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
1001 + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
1002 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
1005 + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
1006 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
1007 ResultSet resultSet = results.getResultSet();) {
1008 while (resultSet.next()) {
1009 if (Case.getCurrentCaseThrows().getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) {
1010 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
1011 } else {
1012 setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
1013 }
1014 }
1015 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
1016 LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
1017
1018 }
1019 }
1020
1021 @Override
1022 public boolean isLeafTypeNode() {
1023 return true;
1024 }
1025
1026 @Override
1027 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1028 return visitor.visit(this);
1029 }
1030
1031 @Override
1032 public String getItemType() {
1033 return getClass().getName();
1034 }
1035
1036 @Subscribe
1037 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1039 }
1040
1041 @Subscribe
1042 void handleDataAdded(ModuleDataEvent event) {
1044 }
1045 }
1046
1047 final private class BINFactory extends ObservingChildren<BinResult> {
1048
1049 private final PropertyChangeListener pcl = new PropertyChangeListener() {
1050 @Override
1051 public void propertyChange(PropertyChangeEvent evt) {
1052 String eventType = evt.getPropertyName();
1053 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
1060 try {
1068 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
1069 if (null != eventData
1070 && eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
1071 reviewStatusBus.post(eventData);
1072 }
1073 } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1074 // Case is closed, do nothing.
1075 }
1076 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
1077 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
1084 try {
1086
1087 refresh(true);
1088 } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1089 // Case is closed, do nothing.
1090 }
1091 }
1092 }
1093 };
1094
1095 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
1096
1097 @Override
1104
1105 @Override
1112
1113 @Subscribe
1114 @Override
1115 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1116 refresh(true);
1117 }
1118
1119 @Subscribe
1120 @Override
1121 void handleDataAdded(ModuleDataEvent event) {
1122 refresh(true);
1123 }
1124
1125 @Override
1126 protected boolean createKeys(List<BinResult> list) {
1127
1128 RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
1129
1130 String query
1131 = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
1132 + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
1133 + " FROM blackboard_artifacts " //NON-NLS
1134 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1135 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
1136 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1139 + " GROUP BY BIN " //NON-NLS
1140 + " ORDER BY BIN "; //NON-NLS
1141 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
1142 ResultSet resultSet = results.getResultSet();) {
1143 //sort all te individual bins in to the ranges
1144 while (resultSet.next()) {
1145 final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
1146 long count = resultSet.getLong("count");
1147
1148 BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
1149 BinResult previousResult = binRanges.get(bin);
1150
1151 if (previousResult != null) {
1152 binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
1153 count += previousResult.getCount();
1154 }
1155
1156 if (binRange == null) {
1157 binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
1158 } else {
1159 binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
1160 }
1161 }
1162 binRanges.asMapOfRanges().values().forEach(list::add);
1163 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
1164 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1165 }
1166
1167 return true;
1168 }
1169
1170 @Override
1171 protected Node[] createNodesForKey(BinResult key) {
1172 return new Node[]{new BINNode(key)};
1173 }
1174 }
1175
1180 final public class ByBINNode extends DisplayableItemNode {
1181
1185 @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
1186 private ByBINNode() {
1187 super(Children.create(new BINFactory(), true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
1188 setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
1190 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1191 reviewStatusBus.register(this);
1192 }
1193
1194 @NbBundle.Messages({
1195 "# {0} - number of children",
1196 "Accounts.ByBINNode.displayName=By BIN ({0})"})
1197 private void updateDisplayName() {
1198 String query
1199 = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1200 + " FROM blackboard_artifacts " //NON-NLS
1201 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1202 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
1203 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1205 + getRejectedArtifactFilterClause(); //NON-NLS
1206 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
1207 ResultSet resultSet = results.getResultSet();) {
1208 while (resultSet.next()) {
1209 setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1210 }
1211 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
1212 LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1213 }
1214 }
1215
1216 @Override
1217 public boolean isLeafTypeNode() {
1218 return false;
1219 }
1220
1221 @Override
1222 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1223 return visitor.visit(this);
1224 }
1225
1226 @Override
1227 public String getItemType() {
1228 return getClass().getName();
1229 }
1230
1231 @Subscribe
1232 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1234 }
1235
1236 @Subscribe
1237 void handleDataAdded(ModuleDataEvent event) {
1239 }
1240 }
1241
1246 @Immutable
1247 final private static class FileWithCCN {
1248
1249 @Override
1250 public int hashCode() {
1251 int hash = 5;
1252 hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1253 hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1254 hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1255 hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1256 hash = 79 * hash + Objects.hashCode(this.statuses);
1257 return hash;
1258 }
1259
1260 @Override
1261 public boolean equals(Object obj) {
1262 if (this == obj) {
1263 return true;
1264 }
1265 if (obj == null) {
1266 return false;
1267 }
1268 if (getClass() != obj.getClass()) {
1269 return false;
1270 }
1271 final FileWithCCN other = (FileWithCCN) obj;
1272 if (this.objID != other.objID) {
1273 return false;
1274 }
1275 if (this.hits != other.hits) {
1276 return false;
1277 }
1278 if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
1279 return false;
1280 }
1281 if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
1282 return false;
1283 }
1284 if (!Objects.equals(this.statuses, other.statuses)) {
1285 return false;
1286 }
1287 return true;
1288 }
1289
1290 private final long objID;
1291 private final String keywordSearchDocID;
1292 private final List<Long> artifactIDs;
1293 private final long hits;
1294 private final Set<BlackboardArtifact.ReviewStatus> statuses;
1295 private final AbstractFile file;
1296
1297 private FileWithCCN(AbstractFile file, long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1298 this.objID = objID;
1299 this.keywordSearchDocID = solrDocID;
1300 this.artifactIDs = artifactIDs;
1301 this.hits = hits;
1302 this.statuses = statuses;
1303 this.file = file;
1304 }
1305
1311 public long getObjID() {
1312 return objID;
1313 }
1314
1321 public String getkeywordSearchDocID() {
1322 return keywordSearchDocID;
1323 }
1324
1330 public List<Long> getArtifactIDs() {
1331 return Collections.unmodifiableList(artifactIDs);
1332 }
1333
1339 public long getHits() {
1340 return hits;
1341 }
1342
1348 public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
1349 return Collections.unmodifiableSet(statuses);
1350 }
1351
1352 AbstractFile getFile() {
1353 return file;
1354 }
1355 }
1356
1373 static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1374 return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1375 : Stream.of(groupConcat.split(",")) //NON-NLS
1376 .map(mapper::apply)
1377 .collect(Collectors.toList());
1378 }
1379
1383 final public class FileWithCCNNode extends DisplayableItemNode {
1384
1385 private final FileWithCCN fileKey;
1386 private final String fileName;
1387
1397 @NbBundle.Messages({
1398 "# {0} - raw file name",
1399 "# {1} - solr chunk id",
1400 "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1401 private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
1402 super(Children.LEAF, Lookups.fixed(lookupContents));
1403 this.fileKey = key;
1404 this.fileName = (key.getkeywordSearchDocID() == null)
1405 ? content.getName()
1406 : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
1407 setName(fileName + key.getObjID());
1408 setDisplayName(fileName);
1409 }
1410
1411 @Override
1412 public boolean isLeafTypeNode() {
1413 return true;
1414 }
1415
1416 @Override
1417 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1418 return visitor.visit(this);
1419 }
1420
1421 @Override
1422 public String getItemType() {
1423 return getClass().getName();
1424 }
1425
1426 @Override
1427 @NbBundle.Messages({
1428 "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1429 "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1430 "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1431 "Accounts.FileWithCCNNode.noDescription=no description"})
1432 protected Sheet createSheet() {
1433 Sheet sheet = super.createSheet();
1434 Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1435 if (propSet == null) {
1436 propSet = Sheet.createPropertiesSet();
1437 sheet.put(propSet);
1438 }
1439
1440 propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1441 Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1442 Bundle.Accounts_FileWithCCNNode_noDescription(),
1443 fileName));
1444 propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1445 Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1446 Bundle.Accounts_FileWithCCNNode_noDescription(),
1447 fileKey.getHits()));
1448 propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1449 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1450 Bundle.Accounts_FileWithCCNNode_noDescription(),
1451 fileKey.getStatuses().stream()
1452 .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1453 .collect(Collectors.joining(", ")))); //NON-NLS
1454
1455 return sheet;
1456 }
1457
1458 @Override
1459 public Action[] getActions(boolean context) {
1460 Action[] actions = super.getActions(context);
1461 ArrayList<Action> arrayList = new ArrayList<>();
1462 try {
1463 arrayList.addAll(DataModelActionsFactory.getActions(Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(fileKey.getObjID()), false));
1464 } catch (TskCoreException | NoCurrentCaseException ex) {
1465 LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1466 }
1467
1468 arrayList.add(approveActionInstance);
1469 arrayList.add(rejectActionInstance);
1470 arrayList.add(null);
1471 arrayList.addAll(Arrays.asList(actions));
1472 return arrayList.toArray(new Action[arrayList.size()]);
1473 }
1474 }
1475
1476 final private class CreditCardNumberFactory extends ObservingChildren<DataArtifact> {
1477
1478 private final BinResult bin;
1479
1481 this.bin = bin;
1482 }
1483
1484 @Subscribe
1485 @Override
1486 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1487 refresh(true);
1488 }
1489
1490 @Subscribe
1491 @Override
1492 void handleDataAdded(ModuleDataEvent event) {
1493 refresh(true);
1494 }
1495
1496 @Override
1497 protected boolean createKeys(List<DataArtifact> list) {
1498
1499 String query
1500 = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS
1501 + " FROM blackboard_artifacts " //NON-NLS
1502 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1503 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
1504 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1505 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1508 + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1509 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
1510 ResultSet rs = results.getResultSet();) {
1511 while (rs.next()) {
1512 list.add(Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getDataArtifactById(rs.getLong("artifact_obj_id"))); //NON-NLS
1513 }
1514 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
1515 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1516
1517 }
1518 return true;
1519 }
1520
1521 @Override
1522 protected Node[] createNodesForKey(DataArtifact artifact) {
1523 return new Node[]{new AccountArtifactNode(artifact)};
1524 }
1525 }
1526
1527 private String getBinRangeString(BinResult bin) {
1528 if (bin.getBINStart() == bin.getBINEnd()) {
1529 return Integer.toString(bin.getBINStart());
1530 } else {
1531 return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1532 }
1533 }
1534
1535 final public class BINNode extends DisplayableItemNode {
1536
1540 private final BinResult bin;
1541
1543 super(Children.create(new CreditCardNumberFactory(bin), true), Lookups.singleton(getBinRangeString(bin)));
1544 this.bin = bin;
1545 setName(getBinRangeString(bin));
1547 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1548 reviewStatusBus.register(this);
1549 }
1550
1551 @Subscribe
1552 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1554 updateSheet();
1555 }
1556
1557 @Subscribe
1558 void handleDataAdded(ModuleDataEvent event) {
1560 }
1561
1562 private void updateDisplayName() {
1563 String query
1564 = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1565 + " FROM blackboard_artifacts " //NON-NLS
1566 + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1567 + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
1568 + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1569 + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1572 try (SleuthkitCase.CaseDbQuery results = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query);
1573 ResultSet resultSet = results.getResultSet();) {
1574 while (resultSet.next()) {
1575 setDisplayName(getBinRangeString(bin) + " (" + resultSet.getLong("count") + ")"); //NON-NLS
1576 }
1577 } catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
1578 LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1579
1580 }
1581
1582 }
1583
1584 @Override
1585 public boolean isLeafTypeNode() {
1586 return true;
1587 }
1588
1589 @Override
1590 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1591 return visitor.visit(this);
1592 }
1593
1594 @Override
1595 public String getItemType() {
1596 return getClass().getName();
1597 }
1598
1599 private Sheet.Set getPropertySet(Sheet sheet) {
1600 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1601 if (sheetSet == null) {
1602 sheetSet = Sheet.createPropertiesSet();
1603 sheet.put(sheetSet);
1604 }
1605 return sheetSet;
1606 }
1607
1608 @Override
1609 @NbBundle.Messages({
1610 "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1611 "Accounts.BINNode.accountsProperty.displayName=Accounts",
1612 "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1613 "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1614 "Accounts.BINNode.brandProperty.displayName=Brand",
1615 "Accounts.BINNode.bankProperty.displayName=Bank",
1616 "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1617 "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1618 "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1619 "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1620 "Accounts.BINNode.noDescription=no description"})
1621 protected Sheet createSheet() {
1622 Sheet sheet = super.createSheet();
1623 Sheet.Set properties = getPropertySet(sheet);
1624
1625 properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1626 Bundle.Accounts_BINNode_binProperty_displayName(),
1627 Bundle.Accounts_BINNode_noDescription(),
1629 properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1630 Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1631 bin.getCount()));
1632
1633 //add optional properties if they are available
1634 if (bin.hasDetails()) {
1635 bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1636 Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1637 cardType)));
1638 bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1639 Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1640 scheme)));
1641 bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1642 Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1643 brand)));
1644 bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1645 Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1646 bankName)));
1647 bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1648 Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1649 bankCity)));
1650 bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1651 Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1652 country)));
1653 bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1654 Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1655 phoneNumber)));
1656 bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1657 Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1658 url)));
1659 }
1660 return sheet;
1661 }
1662
1663 private void updateSheet() {
1664 SwingUtilities.invokeLater(() -> {
1665 this.setSheet(createSheet());
1666 });
1667 }
1668
1669 }
1670
1675 @Immutable
1676 final static private class BinResult implements CreditCards.BankIdentificationNumber {
1677
1678 @Override
1679 public int hashCode() {
1680 int hash = 3;
1681 hash = 97 * hash + this.binEnd;
1682 hash = 97 * hash + this.binStart;
1683 return hash;
1684 }
1685
1686 @Override
1687 public boolean equals(Object obj) {
1688 if (this == obj) {
1689 return true;
1690 }
1691 if (obj == null) {
1692 return false;
1693 }
1694 if (getClass() != obj.getClass()) {
1695 return false;
1696 }
1697 final BinResult other = (BinResult) obj;
1698 if (this.binEnd != other.binEnd) {
1699 return false;
1700 }
1701 if (this.binStart != other.binStart) {
1702 return false;
1703 }
1704 return true;
1705 }
1706
1710 private final long count;
1711
1712 private final BINRange binRange;
1713 private final int binEnd;
1714 private final int binStart;
1715
1716 private BinResult(long count, @Nonnull BINRange binRange) {
1717 this.count = count;
1718 this.binRange = binRange;
1719 binStart = binRange.getBINstart();
1720 binEnd = binRange.getBINend();
1721 }
1722
1723 private BinResult(long count, int start, int end) {
1724 this.count = count;
1725 this.binRange = null;
1726 binStart = start;
1727 binEnd = end;
1728 }
1729
1730 int getBINStart() {
1731 return binStart;
1732 }
1733
1734 int getBINEnd() {
1735 return binEnd;
1736 }
1737
1738 long getCount() {
1739 return count;
1740 }
1741
1742 boolean hasDetails() {
1743 return binRange != null;
1744 }
1745
1746 @Override
1747 public Optional<Integer> getNumberLength() {
1748 return binRange.getNumberLength();
1749 }
1750
1751 @Override
1752 public Optional<String> getBankCity() {
1753 return binRange.getBankCity();
1754 }
1755
1756 @Override
1757 public Optional<String> getBankName() {
1758 return binRange.getBankName();
1759 }
1760
1761 @Override
1762 public Optional<String> getBankPhoneNumber() {
1763 return binRange.getBankPhoneNumber();
1764 }
1765
1766 @Override
1767 public Optional<String> getBankURL() {
1768 return binRange.getBankURL();
1769 }
1770
1771 @Override
1772 public Optional<String> getBrand() {
1773 return binRange.getBrand();
1774 }
1775
1776 @Override
1777 public Optional<String> getCardType() {
1778 return binRange.getCardType();
1779 }
1780
1781 @Override
1782 public Optional<String> getCountry() {
1783 return binRange.getCountry();
1784 }
1785
1786 @Override
1787 public Optional<String> getScheme() {
1788 return binRange.getScheme();
1789 }
1790 }
1791
1792 final private class AccountArtifactNode extends BlackboardArtifactNode {
1793
1794 private final BlackboardArtifact artifact;
1795
1796 private AccountArtifactNode(BlackboardArtifact artifact) {
1797 super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1798 this.artifact = artifact;
1799 setName(Long.toString(this.artifact.getArtifactID()));
1800
1801 reviewStatusBus.register(this);
1802 }
1803
1804 @Override
1805 public Action[] getActions(boolean context) {
1806 List<Action> actionsList = new ArrayList<>();
1807 actionsList.addAll(Arrays.asList(super.getActions(context)));
1808
1809 actionsList.add(approveActionInstance);
1810 actionsList.add(rejectActionInstance);
1811
1812 return actionsList.toArray(new Action[actionsList.size()]);
1813 }
1814
1815 @Override
1816 protected Sheet createSheet() {
1817 Sheet sheet = super.createSheet();
1818 Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1819 if (properties == null) {
1820 properties = Sheet.createPropertiesSet();
1821 sheet.put(properties);
1822 }
1823 properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1824 Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1825 Bundle.Accounts_FileWithCCNNode_noDescription(),
1826 artifact.getReviewStatus().getDisplayName()));
1827
1828 return sheet;
1829 }
1830
1831 @Subscribe
1832 void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1833
1834 // Update the node if event includes this artifact
1835 event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1836 return _item;
1837 }).forEachOrdered((_item) -> {
1838 updateSheet();
1839 });
1840 }
1841
1842 private void updateSheet() {
1843 SwingUtilities.invokeLater(() -> {
1844 this.setSheet(createSheet());
1845 });
1846 }
1847
1848 }
1849
1850 @Deprecated
1851 private final class ToggleShowRejected extends AbstractAction {
1852
1853 @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1854 ToggleShowRejected() {
1855 super(Bundle.ToggleShowRejected_name());
1856 }
1857
1858 @Override
1859 public void actionPerformed(ActionEvent e) {
1861 reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1862 }
1863 }
1864
1870 public void setShowRejected(boolean showRejected) {
1871 this.showRejected = showRejected;
1872 reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1873 }
1874
1875 private abstract class ReviewStatusAction extends AbstractAction {
1876
1877 private final BlackboardArtifact.ReviewStatus newStatus;
1878
1879 private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1880 super(displayName);
1881 this.newStatus = newStatus;
1882
1883 }
1884
1885 @Override
1886 public void actionPerformed(ActionEvent e) {
1887
1888 /*
1889 * get paths for selected nodes to reselect after applying review
1890 * status change
1891 */
1892 List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1893 .map(node -> {
1894 String[] createPath;
1895 /*
1896 * If the we are rejecting and not showing rejected
1897 * results, then the selected node, won't exist any
1898 * more, so we select the previous one in stead.
1899 */
1900 if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1901 List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1902 if (siblings.size() > 1) {
1903 int indexOf = siblings.indexOf(node);
1904 //there is no previous for the first node, so instead we select the next one
1905 Node sibling = indexOf > 0
1906 ? siblings.get(indexOf - 1)
1907 : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1908 createPath = NodeOp.createPath(sibling, null);
1909 } else {
1910 /*
1911 * if there are no other siblings to select,
1912 * just return null, but note we need to filter
1913 * this out of stream below
1914 */
1915 return null;
1916 }
1917 } else {
1918 createPath = NodeOp.createPath(node, null);
1919 }
1920 //for the reselect to work we need to strip off the first part of the path.
1921 return Arrays.copyOfRange(createPath, 1, createPath.length);
1922 })
1923 .filter(Objects::nonNull)
1924 .collect(Collectors.toList());
1925
1926 //change status of selected artifacts
1927 final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1928 artifacts.forEach(artifact -> {
1929 try {
1930 artifact.setReviewStatus(newStatus);
1931 } catch (TskCoreException ex) {
1932 LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1933 }
1934 });
1935 //post event
1937
1939 final Node rootNode = directoryListing.getRootNode();
1940
1941 //convert paths back to nodes
1942 List<Node> toArray = new ArrayList<>();
1943 selectedPaths.forEach(path -> {
1944 try {
1945 toArray.add(NodeOp.findPath(rootNode, path));
1946 } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1947 //just ingnore paths taht don't exist. this is expected since we are rejecting
1948 }
1949 });
1950 //select nodes
1951 directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1952 }
1953 }
1954
1955 final private class ApproveAccounts extends ReviewStatusAction {
1956
1957 @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1959 super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1960 }
1961 }
1962
1963 final private class RejectAccounts extends ReviewStatusAction {
1964
1965 @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1966 private RejectAccounts() {
1967 super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1968 }
1969 }
1970
1971 static private class ReviewStatusChangeEvent {
1972
1973 Collection<? extends BlackboardArtifact> artifacts;
1974 BlackboardArtifact.ReviewStatus newReviewStatus;
1975
1976 ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1977 this.artifacts = artifacts;
1978 this.newReviewStatus = newReviewStatus;
1979 }
1980 }
1981
1987 public static String getIconFilePath(Account.Type type) {
1988
1989 if (type.equals(Account.Type.CREDIT_CARD)) {
1990 return ICON_BASE_PATH + "credit-card.png";
1991 } else if (type.equals(Account.Type.DEVICE)) {
1992 return ICON_BASE_PATH + "image.png";
1993 } else if (type.equals(Account.Type.EMAIL)) {
1994 return ICON_BASE_PATH + "email.png";
1995 } else if (type.equals(Account.Type.FACEBOOK)) {
1996 return ICON_BASE_PATH + "facebook.png";
1997 } else if (type.equals(Account.Type.INSTAGRAM)) {
1998 return ICON_BASE_PATH + "instagram.png";
1999 } else if (type.equals(Account.Type.MESSAGING_APP)) {
2000 return ICON_BASE_PATH + "messaging.png";
2001 } else if (type.equals(Account.Type.PHONE)) {
2002 return ICON_BASE_PATH + "phone.png";
2003 } else if (type.equals(Account.Type.TWITTER)) {
2004 return ICON_BASE_PATH + "twitter.png";
2005 } else if (type.equals(Account.Type.WEBSITE)) {
2006 return ICON_BASE_PATH + "web-file.png";
2007 } else if (type.equals(Account.Type.WHATSAPP)) {
2008 return ICON_BASE_PATH + "WhatsApp.png";
2009 } else {
2010 //there could be a default icon instead...
2011 return ICON_BASE_PATH + "face.png";
2012// throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
2013 }
2014 }
2015}
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
synchronized static Logger getLogger(String name)
Definition Logger.java:124
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types)
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
static synchronized BankIdentificationNumber getBINInfo(int bin)
static List< Action > getActions(File file, boolean isArtifactSource)
BinResult(long count, @Nonnull BINRange binRange)
Set< BlackboardArtifact.ReviewStatus > getStatuses()
final Set< BlackboardArtifact.ReviewStatus > statuses
FileWithCCN(AbstractFile file, long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
boolean createKeys(List< CreditCardViewMode > list)
static String getIconFilePath(Account.Type type)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
Definition Accounts.java:99
static synchronized IngestManager getInstance()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.