Autopsy  4.17.0
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-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datamodel.accounts;
20 
21 import com.google.common.collect.Range;
22 import com.google.common.collect.RangeMap;
23 import com.google.common.collect.TreeRangeMap;
24 import com.google.common.eventbus.EventBus;
25 import com.google.common.eventbus.Subscribe;
26 import java.awt.event.ActionEvent;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.EnumSet;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.Optional;
42 import java.util.Set;
43 import java.util.function.Function;
44 import java.util.logging.Level;
45 import java.util.stream.Collectors;
46 import java.util.stream.Stream;
47 import javax.annotation.Nonnull;
48 import javax.annotation.concurrent.Immutable;
49 import javax.swing.AbstractAction;
50 import javax.swing.Action;
51 import org.apache.commons.lang3.StringUtils;
52 import org.openide.nodes.ChildFactory;
53 import org.openide.nodes.Children;
54 import org.openide.nodes.Node;
55 import org.openide.nodes.NodeNotFoundException;
56 import org.openide.nodes.NodeOp;
57 import org.openide.nodes.Sheet;
58 import org.openide.util.NbBundle;
59 import org.openide.util.Utilities;
60 import org.openide.util.lookup.Lookups;
76 import org.sleuthkit.datamodel.AbstractFile;
77 import org.sleuthkit.datamodel.Account;
78 import org.sleuthkit.datamodel.BlackboardArtifact;
79 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
80 import org.sleuthkit.datamodel.BlackboardAttribute;
81 import org.sleuthkit.datamodel.Content;
82 import org.sleuthkit.datamodel.SleuthkitCase;
83 import org.sleuthkit.datamodel.TskCoreException;
84 import org.sleuthkit.datamodel.TskData.DbType;
85 
90 final public class Accounts implements AutopsyVisitableItem {
91 
92  private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
93  private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
96 
97  @NbBundle.Messages("AccountsRootNode.name=Accounts")
98  final public static String NAME = Bundle.AccountsRootNode_name();
99 
100  private SleuthkitCase skCase;
101  private final long filteringDSObjId; // 0 if not filtering/grouping by data source
102 
103  private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
104 
105  /*
106  * Should rejected accounts be shown in the accounts section of the tree.
107  */
108  private boolean showRejected = false; //NOPMD redundant initializer
109 
112 
113  // tracks the number of each account type found
115 
121  public Accounts(SleuthkitCase skCase) {
122  this(skCase, 0);
123  }
124 
131  public Accounts(SleuthkitCase skCase, long objId) {
132  this.skCase = skCase;
133  this.filteringDSObjId = objId;
134 
135  this.rejectActionInstance = new RejectAccounts();
136  this.approveActionInstance = new ApproveAccounts();
137  this.accountTypeResults = new AccountTypeResults();
138  }
139 
140  @Override
141  public <T> T accept(AutopsyItemVisitor<T> visitor) {
142  return visitor.visit(this);
143  }
144 
153  return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
154  }
155 
162  private String getFilterByDataSourceClause() {
163  if (filteringDSObjId > 0) {
164  return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
165  }
166 
167  return " ";
168  }
169 
177  @Deprecated
178  public Action newToggleShowRejectedAction() {
179  return new ToggleShowRejected();
180  }
181 
188  private abstract class ObservingChildren<X> extends ChildFactory.Detachable<X> {
189 
195  super();
196  }
197 
202  @Override
203  abstract protected boolean createKeys(List<X> list);
204 
210  @Subscribe
211  abstract void handleReviewStatusChange(ReviewStatusChangeEvent event);
212 
213  @Subscribe
214  abstract void handleDataAdded(ModuleDataEvent event);
215 
216  @Override
217  protected void removeNotify() {
218  super.removeNotify();
219  reviewStatusBus.unregister(ObservingChildren.this);
220  }
221 
222  @Override
223  protected void addNotify() {
224  super.addNotify();
225  refresh(true);
226  reviewStatusBus.register(ObservingChildren.this);
227  }
228  }
229 
233  @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
234  final public class AccountsRootNode extends DisplayableItemNode {
235 
236  public AccountsRootNode() {
237  super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
238  setName(Accounts.NAME);
239  setDisplayName(Bundle.Accounts_RootNode_displayName());
240  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
241  }
242 
243  @Override
244  public boolean isLeafTypeNode() {
245  return false;
246  }
247 
248  @Override
249  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
250  return visitor.visit(this);
251  }
252 
253  @Override
254  public String getItemType() {
255  return getClass().getName();
256  }
257  }
258 
262  private class AccountTypeResults {
263  private final Map<String, Long> counts = new HashMap<>();
264 
266  update();
267  }
268 
274  Long getCount(String accountType) {
275  return counts.get(accountType);
276  }
277 
282  List<String> getTypes() {
283  List<String> types = new ArrayList<>(counts.keySet());
284  Collections.sort(types);
285  return types;
286  }
287 
291  private void update() {
292  String accountTypesInUseQuery
293  = "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
294  + " FROM blackboard_artifacts " //NON-NLS
295  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
296  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
297  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
299  + " GROUP BY blackboard_attributes.value_text ";
300 
301  try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
302  ResultSet resultSet = executeQuery.getResultSet()) {
303 
304  counts.clear();
305  while (resultSet.next()) {
306  String accountType = resultSet.getString("account_type");
307  Long count = resultSet.getLong("count");
308  counts.put(accountType, count);
309  }
310  } catch (TskCoreException | SQLException ex) {
311  LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
312  }
313  }
314  }
315 
319  private class AccountTypeFactory extends ObservingChildren<String> {
320 
321  /*
322  * The pcl is in this class because it has the easiest mechanisms to add
323  * and remove itself during its life cycles.
324  */
325  private final PropertyChangeListener pcl = new PropertyChangeListener() {
326  @Override
327  public void propertyChange(PropertyChangeEvent evt) {
328  String eventType = evt.getPropertyName();
329  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
336  try {
344  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
345  if (null != eventData
346  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
347  accountTypeResults.update();
348  reviewStatusBus.post(eventData);
349  }
350  } catch (NoCurrentCaseException notUsed) {
351  // Case is closed, do nothing.
352  }
353  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
354  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
361  try {
363  refresh(true);
364  } catch (NoCurrentCaseException notUsed) {
365  // Case is closed, do nothing.
366  }
367  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
368  // case was closed. Remove listeners so that we don't get called with a stale case handle
369  if (evt.getNewValue() == null) {
370  removeNotify();
371  skCase = null;
372  }
373  }
374  }
375  };
376 
377  @Subscribe
378  @Override
379  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
380  refresh(true);
381  }
382 
383  @Subscribe
384  @Override
385  void handleDataAdded(ModuleDataEvent event) {
386  refresh(true);
387  }
388 
389  @Override
390  protected boolean createKeys(List<String> list) {
391  list.addAll(accountTypeResults.getTypes());
392  return true;
393  }
394 
401  private Node[] getNodeArr(Node node) {
402  reviewStatusBus.register(node);
403  return new Node[]{node};
404  }
405 
406  @Override
407  protected Node[] createNodesForKey(String acountTypeName) {
408 
409  if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) {
411  } else {
412 
413  try {
414  Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName);
415  return getNodeArr(new DefaultAccountTypeNode(accountType));
416  } catch (TskCoreException ex) {
417  LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
418  }
419 
420  return new Node[]{};
421  }
422  }
423 
424  @Override
425  protected void removeNotify() {
429  super.removeNotify();
430  }
431 
432  @Override
433  protected void addNotify() {
437  super.addNotify();
438  refresh(true);
439  }
440 
441  }
442 
443  final private class DefaultAccountFactory extends ObservingChildren<Long> {
444 
445  private final Account.Type accountType;
446 
447  private DefaultAccountFactory(Account.Type accountType) {
448  this.accountType = accountType;
449  }
450 
451  private final PropertyChangeListener pcl = new PropertyChangeListener() {
452  @Override
453  public void propertyChange(PropertyChangeEvent evt) {
454  String eventType = evt.getPropertyName();
455  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
462  try {
470  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
471  if (null != eventData
472  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
473  reviewStatusBus.post(eventData);
474  }
475  } catch (NoCurrentCaseException notUsed) {
476  // Case is closed, do nothing.
477  }
478  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
479  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
486  try {
488  refresh(true);
489 
490  } catch (NoCurrentCaseException notUsed) {
491  // Case is closed, do nothing.
492  }
493  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
494  // case was closed. Remove listeners so that we don't get called with a stale case handle
495  if (evt.getNewValue() == null) {
496  removeNotify();
497  skCase = null;
498  }
499  }
500  }
501  };
502 
503  @Override
504  protected void addNotify() {
508  super.addNotify();
509  }
510 
511  @Override
512  protected void removeNotify() {
516  super.removeNotify();
517  }
518 
519  @Override
520  protected boolean createKeys(List<Long> list) {
521  String query
522  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
523  + " FROM blackboard_artifacts " //NON-NLS
524  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
525  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
526  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
527  + " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
529  + getRejectedArtifactFilterClause(); //NON-NLS
530  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
531  ResultSet rs = results.getResultSet();) {
532  List<Long> tempList = new ArrayList<>();
533  while (rs.next()) {
534  tempList.add(rs.getLong("artifact_id")); // NON-NLS
535  }
536  list.addAll(tempList);
537  } catch (TskCoreException | SQLException ex) {
538  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
539  }
540 
541  return true;
542  }
543 
544  @Override
545  protected Node[] createNodesForKey(Long t) {
546  try {
547  return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))};
548  } catch (TskCoreException ex) {
549  LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
550  return new Node[0];
551  }
552  }
553 
554  @Subscribe
555  @Override
556  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
557  refresh(true);
558  }
559 
560  @Subscribe
561  @Override
562  void handleDataAdded(ModuleDataEvent event) {
563  refresh(true);
564  }
565  }
566 
571  final public class DefaultAccountTypeNode extends DisplayableItemNode {
572  private final Account.Type accountType;
573 
574  private DefaultAccountTypeNode(Account.Type accountType) {
575  super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
576  this.accountType = accountType;
577  String iconPath = getIconFilePath(accountType);
578  this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
579  updateName();
580  }
581 
582  @Override
583  public boolean isLeafTypeNode() {
584  return true;
585  }
586 
587  @Override
588  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
589  return visitor.visit(this);
590  }
591 
592  @Override
593  public String getItemType() {
594  return getClass().getName();
595  }
596 
597 
598  @Subscribe
599  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
600  updateName();
601  }
602 
603  @Subscribe
604  void handleDataAdded(ModuleDataEvent event) {
605  updateName();
606  }
607 
611  public void updateName() {
612  setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
613  }
614  }
615 
619  private enum CreditCardViewMode {
622  }
623 
624  final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
625 
626  private final PropertyChangeListener pcl = new PropertyChangeListener() {
627  @Override
628  public void propertyChange(PropertyChangeEvent evt) {
629  String eventType = evt.getPropertyName();
630  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
637  try {
645  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
646  if (null != eventData
647  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
648  reviewStatusBus.post(eventData);
649  }
650  } catch (NoCurrentCaseException notUsed) {
651  // Case is closed, do nothing.
652  }
653  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
654  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
661  try {
663  refresh(true);
664 
665  } catch (NoCurrentCaseException notUsed) {
666  // Case is closed, do nothing.
667  }
668  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
669  // case was closed. Remove listeners so that we don't get called with a stale case handle
670  if (evt.getNewValue() == null) {
671  removeNotify();
672  skCase = null;
673  }
674  }
675  }
676  };
677 
678  @Subscribe
679  @Override
680  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
681  refresh(true);
682  }
683 
684  @Subscribe
685  @Override
686  void handleDataAdded(ModuleDataEvent event) {
687  refresh(true);
688  }
689 
690  @Override
691  protected void addNotify() {
695  super.addNotify();
696  }
697 
698  @Override
699  protected void removeNotify() {
703  super.removeNotify();
704  }
705 
709  @Override
710  protected boolean createKeys(List<CreditCardViewMode> list) {
711  list.addAll(Arrays.asList(CreditCardViewMode.values()));
712 
713  return true;
714  }
715 
716  @Override
717  protected Node[] createNodesForKey(CreditCardViewMode key) {
718  switch (key) {
719  case BY_BIN:
720  return new Node[]{new ByBINNode()};
721  case BY_FILE:
722  return new Node[]{new ByFileNode()};
723  default:
724  return new Node[0];
725  }
726  }
727  }
728 
733 
739  super(Children.create(new ViewModeFactory(), true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
740  setName(Account.Type.CREDIT_CARD.getDisplayName());
741  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
742  }
743 
747  public void updateName() {
748  setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
749  }
750 
751  @Subscribe
752  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
753  updateName();
754  }
755 
756  @Subscribe
757  void handleDataAdded(ModuleDataEvent event) {
758  updateName();
759  }
760 
761  @Override
762  public boolean isLeafTypeNode() {
763  return false;
764  }
765 
766  @Override
767  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
768  return visitor.visit(this);
769  }
770 
771  @Override
772  public String getItemType() {
773  return getClass().getName();
774  }
775  }
776 
777  final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
778 
779  private final PropertyChangeListener pcl = new PropertyChangeListener() {
780  @Override
781  public void propertyChange(PropertyChangeEvent evt) {
782  String eventType = evt.getPropertyName();
783  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
790  try {
798  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
799  if (null != eventData
800  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
801  reviewStatusBus.post(eventData);
802  }
803  } catch (NoCurrentCaseException notUsed) {
804  // Case is closed, do nothing.
805  }
806  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
807  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
814  try {
816  refresh(true);
817 
818  } catch (NoCurrentCaseException notUsed) {
819  // Case is closed, do nothing.
820  }
821  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
822  // case was closed. Remove listeners so that we don't get called with a stale case handle
823  if (evt.getNewValue() == null) {
824  removeNotify();
825  skCase = null;
826  }
827  }
828  }
829  };
830 
831  @Override
832  protected void addNotify() {
836  super.addNotify();
837  }
838 
839  @Override
840  protected void removeNotify() {
844  super.removeNotify();
845  }
846 
847  @Subscribe
848  @Override
849  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
850  refresh(true);
851  }
852 
853  @Subscribe
854  @Override
855  void handleDataAdded(ModuleDataEvent event) {
856  refresh(true);
857  }
858 
859  @Override
860  protected boolean createKeys(List<FileWithCCN> list) {
861  String query
862  = "SELECT blackboard_artifacts.obj_id," //NON-NLS
863  + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
864  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
865  query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
866  + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
867  } else {
868  query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
869  + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
870  }
871  query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
872  + " FROM blackboard_artifacts " //NON-NLS
873  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
874  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
875  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
876  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
877  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
878  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
881  + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
882  + " ORDER BY hits DESC "; //NON-NLS
883  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
884  ResultSet resultSet = results.getResultSet();) {
885  while (resultSet.next()) {
886  list.add(new FileWithCCN(
887  resultSet.getLong("obj_id"), //NON-NLS
888  resultSet.getString("solr_document_id"), //NON-NLS
889  unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
890  resultSet.getLong("hits"), //NON-NLS
891  new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
892  }
893  } catch (TskCoreException | SQLException ex) {
894  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
895 
896  }
897  return true;
898  }
899 
900  @Override
901  protected Node[] createNodesForKey(FileWithCCN key) {
902  //add all account artifacts for the file and the file itself to the lookup
903  try {
904  List<Object> lookupContents = new ArrayList<>();
905  for (long artId : key.artifactIDs) {
906  lookupContents.add(skCase.getBlackboardArtifact(artId));
907  }
908  AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID());
909  lookupContents.add(abstractFileById);
910  return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
911  } catch (TskCoreException ex) {
912  LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
913  return new Node[0];
914  }
915  }
916  }
917 
922  final public class ByFileNode extends DisplayableItemNode {
923 
927  private ByFileNode() {
928  super(Children.create(new FileWithCCNFactory(), true), Lookups.singleton("By File"));
929  setName("By File"); //NON-NLS
930  updateDisplayName();
931  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
932  reviewStatusBus.register(this);
933  }
934 
935  @NbBundle.Messages({
936  "# {0} - number of children",
937  "Accounts.ByFileNode.displayName=By File ({0})"})
938  private void updateDisplayName() {
939  String query
940  = "SELECT count(*) FROM ( SELECT count(*) AS documents "
941  + " FROM blackboard_artifacts " //NON-NLS
942  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
943  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
944  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
945  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
946  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
947  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
950  + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
951  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
952  ResultSet resultSet = results.getResultSet();) {
953  while (resultSet.next()) {
954  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
955  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
956  } else {
957  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
958  }
959  }
960  } catch (TskCoreException | SQLException ex) {
961  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
962 
963  }
964  }
965 
966  @Override
967  public boolean isLeafTypeNode() {
968  return true;
969  }
970 
971  @Override
972  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
973  return visitor.visit(this);
974  }
975 
976  @Override
977  public String getItemType() {
978  return getClass().getName();
979  }
980 
981  @Subscribe
982  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
983  updateDisplayName();
984  }
985 
986  @Subscribe
987  void handleDataAdded(ModuleDataEvent event) {
988  updateDisplayName();
989  }
990  }
991 
992  final private class BINFactory extends ObservingChildren<BinResult> {
993 
994  private final PropertyChangeListener pcl = new PropertyChangeListener() {
995  @Override
996  public void propertyChange(PropertyChangeEvent evt) {
997  String eventType = evt.getPropertyName();
998  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
1005  try {
1013  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
1014  if (null != eventData
1015  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
1016  reviewStatusBus.post(eventData);
1017  }
1018  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1019  // Case is closed, do nothing.
1020  }
1021  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
1022  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
1029  try {
1031 
1032  refresh(true);
1033  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1034  // Case is closed, do nothing.
1035  }
1036  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
1037  && (evt.getNewValue() == null)) {
1038  // case was closed. Remove listeners so that we don't get called with a stale case handle
1039  removeNotify();
1040  skCase = null;
1041  }
1042  }
1043  };
1044 
1045  @Override
1046  protected void addNotify() {
1050  super.addNotify();
1051  }
1052 
1053  @Override
1054  protected void removeNotify() {
1058  super.removeNotify();
1059  }
1060 
1061  @Subscribe
1062  @Override
1063  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1064  refresh(true);
1065  }
1066 
1067  @Subscribe
1068  @Override
1069  void handleDataAdded(ModuleDataEvent event) {
1070  refresh(true);
1071  }
1072 
1073  @Override
1074  protected boolean createKeys(List<BinResult> list) {
1075 
1076  RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
1077 
1078  String query
1079  = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
1080  + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
1081  + " FROM blackboard_artifacts " //NON-NLS
1082  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1083  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1084  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1087  + " GROUP BY BIN " //NON-NLS
1088  + " ORDER BY BIN "; //NON-NLS
1089  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1090  ResultSet resultSet = results.getResultSet();) {
1091  //sort all te individual bins in to the ranges
1092  while (resultSet.next()) {
1093  final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
1094  long count = resultSet.getLong("count");
1095 
1096  BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
1097  BinResult previousResult = binRanges.get(bin);
1098 
1099  if (previousResult != null) {
1100  binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
1101  count += previousResult.getCount();
1102  }
1103 
1104  if (binRange == null) {
1105  binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
1106  } else {
1107  binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
1108  }
1109  }
1110  binRanges.asMapOfRanges().values().forEach(list::add);
1111  } catch (TskCoreException | SQLException ex) {
1112  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1113  }
1114 
1115  return true;
1116  }
1117 
1118  @Override
1119  protected Node[] createNodesForKey(BinResult key) {
1120  return new Node[]{new BINNode(key)};
1121  }
1122  }
1123 
1128  final public class ByBINNode extends DisplayableItemNode {
1129 
1133  @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
1134  private ByBINNode() {
1135  super(Children.create(new BINFactory(), true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
1136  setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
1137  updateDisplayName();
1138  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1139  reviewStatusBus.register(this);
1140  }
1141 
1142  @NbBundle.Messages({
1143  "# {0} - number of children",
1144  "Accounts.ByBINNode.displayName=By BIN ({0})"})
1145  private void updateDisplayName() {
1146  String query
1147  = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1148  + " FROM blackboard_artifacts " //NON-NLS
1149  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1150  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1151  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1153  + getRejectedArtifactFilterClause(); //NON-NLS
1154  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1155  ResultSet resultSet = results.getResultSet();) {
1156  while (resultSet.next()) {
1157  setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1158  }
1159  } catch (TskCoreException | SQLException ex) {
1160  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1161  }
1162  }
1163 
1164  @Override
1165  public boolean isLeafTypeNode() {
1166  return false;
1167  }
1168 
1169  @Override
1170  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1171  return visitor.visit(this);
1172  }
1173 
1174  @Override
1175  public String getItemType() {
1176  return getClass().getName();
1177  }
1178 
1179  @Subscribe
1180  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1181  updateDisplayName();
1182  }
1183 
1184  @Subscribe
1185  void handleDataAdded(ModuleDataEvent event) {
1186  updateDisplayName();
1187  }
1188  }
1189 
1194  @Immutable
1195  final private static class FileWithCCN {
1196 
1197  @Override
1198  public int hashCode() {
1199  int hash = 5;
1200  hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1201  hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1202  hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1203  hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1204  hash = 79 * hash + Objects.hashCode(this.statuses);
1205  return hash;
1206  }
1207 
1208  @Override
1209  public boolean equals(Object obj) {
1210  if (this == obj) {
1211  return true;
1212  }
1213  if (obj == null) {
1214  return false;
1215  }
1216  if (getClass() != obj.getClass()) {
1217  return false;
1218  }
1219  final FileWithCCN other = (FileWithCCN) obj;
1220  if (this.objID != other.objID) {
1221  return false;
1222  }
1223  if (this.hits != other.hits) {
1224  return false;
1225  }
1226  if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
1227  return false;
1228  }
1229  if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
1230  return false;
1231  }
1232  if (!Objects.equals(this.statuses, other.statuses)) {
1233  return false;
1234  }
1235  return true;
1236  }
1237 
1238  private final long objID;
1239  private final String keywordSearchDocID;
1240  private final List<Long> artifactIDs;
1241  private final long hits;
1242  private final Set<BlackboardArtifact.ReviewStatus> statuses;
1243 
1244  private FileWithCCN(long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1245  this.objID = objID;
1246  this.keywordSearchDocID = solrDocID;
1247  this.artifactIDs = artifactIDs;
1248  this.hits = hits;
1249  this.statuses = statuses;
1250  }
1251 
1257  public long getObjID() {
1258  return objID;
1259  }
1260 
1267  public String getkeywordSearchDocID() {
1268  return keywordSearchDocID;
1269  }
1270 
1276  public List<Long> getArtifactIDs() {
1277  return Collections.unmodifiableList(artifactIDs);
1278  }
1279 
1285  public long getHits() {
1286  return hits;
1287  }
1288 
1294  public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
1295  return Collections.unmodifiableSet(statuses);
1296  }
1297  }
1298 
1315  static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1316  return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1317  : Stream.of(groupConcat.split(",")) //NON-NLS
1318  .map(mapper::apply)
1319  .collect(Collectors.toList());
1320  }
1321 
1325  final public class FileWithCCNNode extends DisplayableItemNode {
1326 
1327  private final FileWithCCN fileKey;
1328  private final String fileName;
1329 
1339  @NbBundle.Messages({
1340  "# {0} - raw file name",
1341  "# {1} - solr chunk id",
1342  "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1343  private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
1344  super(Children.LEAF, Lookups.fixed(lookupContents));
1345  this.fileKey = key;
1346  this.fileName = (key.getkeywordSearchDocID() == null)
1347  ? content.getName()
1348  : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
1349  setName(fileName + key.getObjID());
1350  setDisplayName(fileName);
1351  }
1352 
1353  @Override
1354  public boolean isLeafTypeNode() {
1355  return true;
1356  }
1357 
1358  @Override
1359  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1360  return visitor.visit(this);
1361  }
1362 
1363  @Override
1364  public String getItemType() {
1365  return getClass().getName();
1366  }
1367 
1368  @Override
1369  @NbBundle.Messages({
1370  "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1371  "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1372  "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1373  "Accounts.FileWithCCNNode.noDescription=no description"})
1374  protected Sheet createSheet() {
1375  Sheet sheet = super.createSheet();
1376  Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1377  if (propSet == null) {
1378  propSet = Sheet.createPropertiesSet();
1379  sheet.put(propSet);
1380  }
1381 
1382  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1383  Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1384  Bundle.Accounts_FileWithCCNNode_noDescription(),
1385  fileName));
1386  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1387  Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1388  Bundle.Accounts_FileWithCCNNode_noDescription(),
1389  fileKey.getHits()));
1390  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1391  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1392  Bundle.Accounts_FileWithCCNNode_noDescription(),
1393  fileKey.getStatuses().stream()
1394  .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1395  .collect(Collectors.joining(", ")))); //NON-NLS
1396 
1397  return sheet;
1398  }
1399 
1400  @Override
1401  public Action[] getActions(boolean context) {
1402  Action[] actions = super.getActions(context);
1403  ArrayList<Action> arrayList = new ArrayList<>();
1404  arrayList.addAll(Arrays.asList(actions));
1405  try {
1406  arrayList.addAll(DataModelActionsFactory.getActions(Accounts.this.skCase.getContentById(fileKey.getObjID()), false));
1407  } catch (TskCoreException ex) {
1408  LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1409  }
1410 
1411  arrayList.add(approveActionInstance);
1412  arrayList.add(rejectActionInstance);
1413 
1414  return arrayList.toArray(new Action[arrayList.size()]);
1415  }
1416  }
1417 
1418  final private class CreditCardNumberFactory extends ObservingChildren<Long> {
1419 
1420  private final BinResult bin;
1421 
1423  this.bin = bin;
1424  }
1425 
1426  @Subscribe
1427  @Override
1428  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1429  refresh(true);
1430  }
1431 
1432  @Subscribe
1433  @Override
1434  void handleDataAdded(ModuleDataEvent event) {
1435  refresh(true);
1436  }
1437 
1438  @Override
1439  protected boolean createKeys(List<Long> list) {
1440 
1441  String query
1442  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1443  + " FROM blackboard_artifacts " //NON-NLS
1444  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1445  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1446  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1447  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1450  + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1451  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1452  ResultSet rs = results.getResultSet();) {
1453  while (rs.next()) {
1454  list.add(rs.getLong("artifact_id")); //NON-NLS
1455  }
1456  } catch (TskCoreException | SQLException ex) {
1457  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1458 
1459  }
1460  return true;
1461  }
1462 
1463  @Override
1464  protected Node[] createNodesForKey(Long artifactID) {
1465  if (skCase == null) {
1466  return new Node[0];
1467  }
1468 
1469  try {
1470  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1471  return new Node[]{new AccountArtifactNode(art)};
1472  } catch (TskCoreException ex) {
1473  LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1474  return new Node[0];
1475  }
1476  }
1477  }
1478 
1479  private String getBinRangeString(BinResult bin) {
1480  if (bin.getBINStart() == bin.getBINEnd()) {
1481  return Integer.toString(bin.getBINStart());
1482  } else {
1483  return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1484  }
1485  }
1486 
1487  final public class BINNode extends DisplayableItemNode {
1488 
1492  private final BinResult bin;
1493 
1494  private BINNode(BinResult bin) {
1495  super(Children.create(new CreditCardNumberFactory(bin), true), Lookups.singleton(getBinRangeString(bin)));
1496  this.bin = bin;
1497  setName(getBinRangeString(bin));
1498  updateDisplayName();
1499  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1500  reviewStatusBus.register(this);
1501  }
1502 
1503  @Subscribe
1504  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1505  updateDisplayName();
1506  updateSheet();
1507  }
1508 
1509  @Subscribe
1510  void handleDataAdded(ModuleDataEvent event) {
1511  updateDisplayName();
1512  }
1513 
1514  private void updateDisplayName() {
1515  String query
1516  = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1517  + " FROM blackboard_artifacts " //NON-NLS
1518  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1519  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1520  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1521  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1524  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1525  ResultSet resultSet = results.getResultSet();) {
1526  while (resultSet.next()) {
1527  setDisplayName(getBinRangeString(bin) + " (" + resultSet.getLong("count") + ")"); //NON-NLS
1528  }
1529  } catch (TskCoreException | SQLException ex) {
1530  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1531 
1532  }
1533 
1534  }
1535 
1536  @Override
1537  public boolean isLeafTypeNode() {
1538  return true;
1539  }
1540 
1541  @Override
1542  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1543  return visitor.visit(this);
1544  }
1545 
1546  @Override
1547  public String getItemType() {
1548  return getClass().getName();
1549  }
1550 
1551  private Sheet.Set getPropertySet(Sheet sheet) {
1552  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1553  if (sheetSet == null) {
1554  sheetSet = Sheet.createPropertiesSet();
1555  sheet.put(sheetSet);
1556  }
1557  return sheetSet;
1558  }
1559 
1560  @Override
1561  @NbBundle.Messages({
1562  "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1563  "Accounts.BINNode.accountsProperty.displayName=Accounts",
1564  "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1565  "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1566  "Accounts.BINNode.brandProperty.displayName=Brand",
1567  "Accounts.BINNode.bankProperty.displayName=Bank",
1568  "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1569  "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1570  "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1571  "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1572  "Accounts.BINNode.noDescription=no description"})
1573  protected Sheet createSheet() {
1574  Sheet sheet = super.createSheet();
1575  Sheet.Set properties = getPropertySet(sheet);
1576 
1577  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1578  Bundle.Accounts_BINNode_binProperty_displayName(),
1579  Bundle.Accounts_BINNode_noDescription(),
1580  getBinRangeString(bin)));
1581  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1582  Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1583  bin.getCount()));
1584 
1585  //add optional properties if they are available
1586  if (bin.hasDetails()) {
1587  bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1588  Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1589  cardType)));
1590  bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1591  Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1592  scheme)));
1593  bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1594  Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1595  brand)));
1596  bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1597  Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1598  bankName)));
1599  bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1600  Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1601  bankCity)));
1602  bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1603  Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1604  country)));
1605  bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1606  Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1607  phoneNumber)));
1608  bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1609  Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1610  url)));
1611  }
1612  return sheet;
1613  }
1614 
1615  private void updateSheet() {
1616  this.setSheet(createSheet());
1617  }
1618 
1619  }
1620 
1625  @Immutable
1626  final static private class BinResult implements CreditCards.BankIdentificationNumber {
1627 
1628  @Override
1629  public int hashCode() {
1630  int hash = 3;
1631  hash = 97 * hash + this.binEnd;
1632  hash = 97 * hash + this.binStart;
1633  return hash;
1634  }
1635 
1636  @Override
1637  public boolean equals(Object obj) {
1638  if (this == obj) {
1639  return true;
1640  }
1641  if (obj == null) {
1642  return false;
1643  }
1644  if (getClass() != obj.getClass()) {
1645  return false;
1646  }
1647  final BinResult other = (BinResult) obj;
1648  if (this.binEnd != other.binEnd) {
1649  return false;
1650  }
1651  if (this.binStart != other.binStart) {
1652  return false;
1653  }
1654  return true;
1655  }
1656 
1660  private final long count;
1661 
1662  private final BINRange binRange;
1663  private final int binEnd;
1664  private final int binStart;
1665 
1666  private BinResult(long count, @Nonnull BINRange binRange) {
1667  this.count = count;
1668  this.binRange = binRange;
1669  binStart = binRange.getBINstart();
1670  binEnd = binRange.getBINend();
1671  }
1672 
1673  private BinResult(long count, int start, int end) {
1674  this.count = count;
1675  this.binRange = null;
1676  binStart = start;
1677  binEnd = end;
1678  }
1679 
1680  int getBINStart() {
1681  return binStart;
1682  }
1683 
1684  int getBINEnd() {
1685  return binEnd;
1686  }
1687 
1688  long getCount() {
1689  return count;
1690  }
1691 
1692  boolean hasDetails() {
1693  return binRange != null;
1694  }
1695 
1696  @Override
1697  public Optional<Integer> getNumberLength() {
1698  return binRange.getNumberLength();
1699  }
1700 
1701  @Override
1702  public Optional<String> getBankCity() {
1703  return binRange.getBankCity();
1704  }
1705 
1706  @Override
1707  public Optional<String> getBankName() {
1708  return binRange.getBankName();
1709  }
1710 
1711  @Override
1712  public Optional<String> getBankPhoneNumber() {
1713  return binRange.getBankPhoneNumber();
1714  }
1715 
1716  @Override
1717  public Optional<String> getBankURL() {
1718  return binRange.getBankURL();
1719  }
1720 
1721  @Override
1722  public Optional<String> getBrand() {
1723  return binRange.getBrand();
1724  }
1725 
1726  @Override
1727  public Optional<String> getCardType() {
1728  return binRange.getCardType();
1729  }
1730 
1731  @Override
1732  public Optional<String> getCountry() {
1733  return binRange.getCountry();
1734  }
1735 
1736  @Override
1737  public Optional<String> getScheme() {
1738  return binRange.getScheme();
1739  }
1740  }
1741 
1742  final private class AccountArtifactNode extends BlackboardArtifactNode {
1743 
1744  private final BlackboardArtifact artifact;
1745 
1746  private AccountArtifactNode(BlackboardArtifact artifact) {
1747  super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1748  this.artifact = artifact;
1749  setName(Long.toString(this.artifact.getArtifactID()));
1750 
1751  reviewStatusBus.register(this);
1752  }
1753 
1754  @Override
1755  public Action[] getActions(boolean context) {
1756  List<Action> actionsList = new ArrayList<>();
1757  actionsList.addAll(Arrays.asList(super.getActions(context)));
1758 
1759  actionsList.add(approveActionInstance);
1760  actionsList.add(rejectActionInstance);
1761 
1762  return actionsList.toArray(new Action[actionsList.size()]);
1763  }
1764 
1765  @Override
1766  protected Sheet createSheet() {
1767  Sheet sheet = super.createSheet();
1768  Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1769  if (properties == null) {
1770  properties = Sheet.createPropertiesSet();
1771  sheet.put(properties);
1772  }
1773  properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1774  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1775  Bundle.Accounts_FileWithCCNNode_noDescription(),
1776  artifact.getReviewStatus().getDisplayName()));
1777 
1778  return sheet;
1779  }
1780 
1781  @Subscribe
1782  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1783 
1784  // Update the node if event includes this artifact
1785  event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1786  return _item;
1787  }).forEachOrdered((_item) -> {
1788  updateSheet();
1789  });
1790  }
1791 
1792  private void updateSheet() {
1793  this.setSheet(createSheet());
1794  }
1795 
1796  }
1797 
1798  @Deprecated
1799  private final class ToggleShowRejected extends AbstractAction {
1800 
1801  @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1802  ToggleShowRejected() {
1803  super(Bundle.ToggleShowRejected_name());
1804  }
1805 
1806  @Override
1807  public void actionPerformed(ActionEvent e) {
1808  showRejected = !showRejected;
1809  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1810  }
1811  }
1812 
1818  public void setShowRejected(boolean showRejected) {
1819  this.showRejected = showRejected;
1820  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1821  }
1822 
1823  private abstract class ReviewStatusAction extends AbstractAction {
1824 
1825  private final BlackboardArtifact.ReviewStatus newStatus;
1826 
1827  private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1828  super(displayName);
1829  this.newStatus = newStatus;
1830 
1831  }
1832 
1833  @Override
1834  public void actionPerformed(ActionEvent e) {
1835 
1836  /*
1837  * get paths for selected nodes to reselect after applying review
1838  * status change
1839  */
1840  List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1841  .map(node -> {
1842  String[] createPath;
1843  /*
1844  * If the we are rejecting and not showing rejected
1845  * results, then the selected node, won't exist any
1846  * more, so we select the previous one in stead.
1847  */
1848  if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1849  List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1850  if (siblings.size() > 1) {
1851  int indexOf = siblings.indexOf(node);
1852  //there is no previous for the first node, so instead we select the next one
1853  Node sibling = indexOf > 0
1854  ? siblings.get(indexOf - 1)
1855  : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1856  createPath = NodeOp.createPath(sibling, null);
1857  } else {
1858  /*
1859  * if there are no other siblings to select,
1860  * just return null, but note we need to filter
1861  * this out of stream below
1862  */
1863  return null;
1864  }
1865  } else {
1866  createPath = NodeOp.createPath(node, null);
1867  }
1868  //for the reselect to work we need to strip off the first part of the path.
1869  return Arrays.copyOfRange(createPath, 1, createPath.length);
1870  })
1871  .filter(Objects::nonNull)
1872  .collect(Collectors.toList());
1873 
1874  //change status of selected artifacts
1875  final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1876  artifacts.forEach(artifact -> {
1877  try {
1878  artifact.setReviewStatus(newStatus);
1879  } catch (TskCoreException ex) {
1880  LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1881  }
1882  });
1883  //post event
1884  reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1885 
1886  final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1887  final Node rootNode = directoryListing.getRootNode();
1888 
1889  //convert paths back to nodes
1890  List<Node> toArray = new ArrayList<>();
1891  selectedPaths.forEach(path -> {
1892  try {
1893  toArray.add(NodeOp.findPath(rootNode, path));
1894  } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1895  //just ingnore paths taht don't exist. this is expected since we are rejecting
1896  }
1897  });
1898  //select nodes
1899  directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1900  }
1901  }
1902 
1903  final private class ApproveAccounts extends ReviewStatusAction {
1904 
1905  @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1906  private ApproveAccounts() {
1907  super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1908  }
1909  }
1910 
1911  final private class RejectAccounts extends ReviewStatusAction {
1912 
1913  @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1914  private RejectAccounts() {
1915  super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1916  }
1917  }
1918 
1919  static private class ReviewStatusChangeEvent {
1920 
1921  Collection<? extends BlackboardArtifact> artifacts;
1922  BlackboardArtifact.ReviewStatus newReviewStatus;
1923 
1924  ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1925  this.artifacts = artifacts;
1926  this.newReviewStatus = newReviewStatus;
1927  }
1928  }
1929 
1935  public static String getIconFilePath(Account.Type type) {
1936 
1937  if (type.equals(Account.Type.CREDIT_CARD)) {
1938  return ICON_BASE_PATH + "credit-card.png";
1939  } else if (type.equals(Account.Type.DEVICE)) {
1940  return ICON_BASE_PATH + "image.png";
1941  } else if (type.equals(Account.Type.EMAIL)) {
1942  return ICON_BASE_PATH + "email.png";
1943  } else if (type.equals(Account.Type.FACEBOOK)) {
1944  return ICON_BASE_PATH + "facebook.png";
1945  } else if (type.equals(Account.Type.INSTAGRAM)) {
1946  return ICON_BASE_PATH + "instagram.png";
1947  } else if (type.equals(Account.Type.MESSAGING_APP)) {
1948  return ICON_BASE_PATH + "messaging.png";
1949  } else if (type.equals(Account.Type.PHONE)) {
1950  return ICON_BASE_PATH + "phone.png";
1951  } else if (type.equals(Account.Type.TWITTER)) {
1952  return ICON_BASE_PATH + "twitter.png";
1953  } else if (type.equals(Account.Type.WEBSITE)) {
1954  return ICON_BASE_PATH + "web-file.png";
1955  } else if (type.equals(Account.Type.WHATSAPP)) {
1956  return ICON_BASE_PATH + "WhatsApp.png";
1957  } else {
1958  //there could be a default icon instead...
1959  return ICON_BASE_PATH + "face.png";
1960 // throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
1961  }
1962  }
1963 }
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
Definition: Accounts.java:95
boolean createKeys(List< CreditCardViewMode > list)
Definition: Accounts.java:710
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
Definition: Accounts.java:1294
static List< Action > getActions(File file, boolean isArtifactSource)
static synchronized BankIdentificationNumber getBINInfo(int bin)
static String getIconFilePath(Account.Type type)
Definition: Accounts.java:1935
void removeIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.ReviewStatus > statuses
Definition: Accounts.java:1242
void addIngestJobEventListener(final PropertyChangeListener listener)
BinResult(long count,@Nonnull BINRange binRange)
Definition: Accounts.java:1666
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Definition: Accounts.java:1343
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
Definition: Accounts.java:1244
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
Accounts(SleuthkitCase skCase, long objId)
Definition: Accounts.java:131
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:491
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
Definition: Accounts.java:94
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
Definition: Accounts.java:1827
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:536

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.