Autopsy  4.15.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  while (rs.next()) {
533  list.add(rs.getLong("artifact_id")); //NON-NLS
534  }
535  } catch (TskCoreException | SQLException ex) {
536  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
537  }
538 
539  return true;
540  }
541 
542  @Override
543  protected Node[] createNodesForKey(Long t) {
544  try {
545  return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))};
546  } catch (TskCoreException ex) {
547  LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
548  return new Node[0];
549  }
550  }
551 
552  @Subscribe
553  @Override
554  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
555  refresh(true);
556  }
557 
558  @Subscribe
559  @Override
560  void handleDataAdded(ModuleDataEvent event) {
561  refresh(true);
562  }
563  }
564 
569  final public class DefaultAccountTypeNode extends DisplayableItemNode {
570  private final Account.Type accountType;
571 
572  private DefaultAccountTypeNode(Account.Type accountType) {
573  super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
574  this.accountType = accountType;
575  String iconPath = getIconFilePath(accountType);
576  this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
577  updateName();
578  }
579 
580  @Override
581  public boolean isLeafTypeNode() {
582  return true;
583  }
584 
585  @Override
586  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
587  return visitor.visit(this);
588  }
589 
590  @Override
591  public String getItemType() {
592  return getClass().getName();
593  }
594 
595 
596  @Subscribe
597  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
598  updateName();
599  }
600 
601  @Subscribe
602  void handleDataAdded(ModuleDataEvent event) {
603  updateName();
604  }
605 
609  public void updateName() {
610  setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
611  }
612  }
613 
617  private enum CreditCardViewMode {
620  }
621 
622  final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
623 
624  private final PropertyChangeListener pcl = new PropertyChangeListener() {
625  @Override
626  public void propertyChange(PropertyChangeEvent evt) {
627  String eventType = evt.getPropertyName();
628  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
635  try {
643  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
644  if (null != eventData
645  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
646  reviewStatusBus.post(eventData);
647  }
648  } catch (NoCurrentCaseException notUsed) {
649  // Case is closed, do nothing.
650  }
651  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
652  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
659  try {
661  refresh(true);
662 
663  } catch (NoCurrentCaseException notUsed) {
664  // Case is closed, do nothing.
665  }
666  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
667  // case was closed. Remove listeners so that we don't get called with a stale case handle
668  if (evt.getNewValue() == null) {
669  removeNotify();
670  skCase = null;
671  }
672  }
673  }
674  };
675 
676  @Subscribe
677  @Override
678  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
679  refresh(true);
680  }
681 
682  @Subscribe
683  @Override
684  void handleDataAdded(ModuleDataEvent event) {
685  refresh(true);
686  }
687 
688  @Override
689  protected void addNotify() {
693  super.addNotify();
694  }
695 
696  @Override
697  protected void removeNotify() {
701  super.removeNotify();
702  }
703 
707  @Override
708  protected boolean createKeys(List<CreditCardViewMode> list) {
709  list.addAll(Arrays.asList(CreditCardViewMode.values()));
710 
711  return true;
712  }
713 
714  @Override
715  protected Node[] createNodesForKey(CreditCardViewMode key) {
716  switch (key) {
717  case BY_BIN:
718  return new Node[]{new ByBINNode()};
719  case BY_FILE:
720  return new Node[]{new ByFileNode()};
721  default:
722  return new Node[0];
723  }
724  }
725  }
726 
731 
737  super(Children.create(new ViewModeFactory(), true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
738  setName(Account.Type.CREDIT_CARD.getDisplayName());
739  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
740  }
741 
745  public void updateName() {
746  setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
747  }
748 
749  @Subscribe
750  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
751  updateName();
752  }
753 
754  @Subscribe
755  void handleDataAdded(ModuleDataEvent event) {
756  updateName();
757  }
758 
759  @Override
760  public boolean isLeafTypeNode() {
761  return false;
762  }
763 
764  @Override
765  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
766  return visitor.visit(this);
767  }
768 
769  @Override
770  public String getItemType() {
771  return getClass().getName();
772  }
773  }
774 
775  final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
776 
777  private final PropertyChangeListener pcl = new PropertyChangeListener() {
778  @Override
779  public void propertyChange(PropertyChangeEvent evt) {
780  String eventType = evt.getPropertyName();
781  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
788  try {
796  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
797  if (null != eventData
798  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
799  reviewStatusBus.post(eventData);
800  }
801  } catch (NoCurrentCaseException notUsed) {
802  // Case is closed, do nothing.
803  }
804  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
805  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
812  try {
814  refresh(true);
815 
816  } catch (NoCurrentCaseException notUsed) {
817  // Case is closed, do nothing.
818  }
819  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
820  // case was closed. Remove listeners so that we don't get called with a stale case handle
821  if (evt.getNewValue() == null) {
822  removeNotify();
823  skCase = null;
824  }
825  }
826  }
827  };
828 
829  @Override
830  protected void addNotify() {
834  super.addNotify();
835  }
836 
837  @Override
838  protected void removeNotify() {
842  super.removeNotify();
843  }
844 
845  @Subscribe
846  @Override
847  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
848  refresh(true);
849  }
850 
851  @Subscribe
852  @Override
853  void handleDataAdded(ModuleDataEvent event) {
854  refresh(true);
855  }
856 
857  @Override
858  protected boolean createKeys(List<FileWithCCN> list) {
859  String query
860  = "SELECT blackboard_artifacts.obj_id," //NON-NLS
861  + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
862  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
863  query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
864  + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
865  } else {
866  query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
867  + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
868  }
869  query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
870  + " FROM blackboard_artifacts " //NON-NLS
871  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
872  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
873  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
874  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
875  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
876  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
879  + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
880  + " ORDER BY hits DESC "; //NON-NLS
881  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
882  ResultSet resultSet = results.getResultSet();) {
883  while (resultSet.next()) {
884  list.add(new FileWithCCN(
885  resultSet.getLong("obj_id"), //NON-NLS
886  resultSet.getString("solr_document_id"), //NON-NLS
887  unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
888  resultSet.getLong("hits"), //NON-NLS
889  new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
890  }
891  } catch (TskCoreException | SQLException ex) {
892  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
893 
894  }
895  return true;
896  }
897 
898  @Override
899  protected Node[] createNodesForKey(FileWithCCN key) {
900  //add all account artifacts for the file and the file itself to the lookup
901  try {
902  List<Object> lookupContents = new ArrayList<>();
903  for (long artId : key.artifactIDs) {
904  lookupContents.add(skCase.getBlackboardArtifact(artId));
905  }
906  AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID());
907  lookupContents.add(abstractFileById);
908  return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
909  } catch (TskCoreException ex) {
910  LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
911  return new Node[0];
912  }
913  }
914  }
915 
920  final public class ByFileNode extends DisplayableItemNode {
921 
925  private ByFileNode() {
926  super(Children.create(new FileWithCCNFactory(), true), Lookups.singleton("By File"));
927  setName("By File"); //NON-NLS
928  updateDisplayName();
929  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
930  reviewStatusBus.register(this);
931  }
932 
933  @NbBundle.Messages({
934  "# {0} - number of children",
935  "Accounts.ByFileNode.displayName=By File ({0})"})
936  private void updateDisplayName() {
937  String query
938  = "SELECT count(*) FROM ( SELECT count(*) AS documents "
939  + " FROM blackboard_artifacts " //NON-NLS
940  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
941  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
942  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
943  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
944  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
945  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
948  + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
949  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
950  ResultSet resultSet = results.getResultSet();) {
951  while (resultSet.next()) {
952  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
953  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
954  } else {
955  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
956  }
957  }
958  } catch (TskCoreException | SQLException ex) {
959  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
960 
961  }
962  }
963 
964  @Override
965  public boolean isLeafTypeNode() {
966  return true;
967  }
968 
969  @Override
970  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
971  return visitor.visit(this);
972  }
973 
974  @Override
975  public String getItemType() {
976  return getClass().getName();
977  }
978 
979  @Subscribe
980  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
981  updateDisplayName();
982  }
983 
984  @Subscribe
985  void handleDataAdded(ModuleDataEvent event) {
986  updateDisplayName();
987  }
988  }
989 
990  final private class BINFactory extends ObservingChildren<BinResult> {
991 
992  private final PropertyChangeListener pcl = new PropertyChangeListener() {
993  @Override
994  public void propertyChange(PropertyChangeEvent evt) {
995  String eventType = evt.getPropertyName();
996  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
1003  try {
1011  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
1012  if (null != eventData
1013  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
1014  reviewStatusBus.post(eventData);
1015  }
1016  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1017  // Case is closed, do nothing.
1018  }
1019  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
1020  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
1027  try {
1029 
1030  refresh(true);
1031  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
1032  // Case is closed, do nothing.
1033  }
1034  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
1035  && (evt.getNewValue() == null)) {
1036  // case was closed. Remove listeners so that we don't get called with a stale case handle
1037  removeNotify();
1038  skCase = null;
1039  }
1040  }
1041  };
1042 
1043  @Override
1044  protected void addNotify() {
1048  super.addNotify();
1049  }
1050 
1051  @Override
1052  protected void removeNotify() {
1056  super.removeNotify();
1057  }
1058 
1059  @Subscribe
1060  @Override
1061  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1062  refresh(true);
1063  }
1064 
1065  @Subscribe
1066  @Override
1067  void handleDataAdded(ModuleDataEvent event) {
1068  refresh(true);
1069  }
1070 
1071  @Override
1072  protected boolean createKeys(List<BinResult> list) {
1073 
1074  RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
1075 
1076  String query
1077  = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
1078  + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
1079  + " FROM blackboard_artifacts " //NON-NLS
1080  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1081  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1082  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1085  + " GROUP BY BIN " //NON-NLS
1086  + " ORDER BY BIN "; //NON-NLS
1087  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1088  ResultSet resultSet = results.getResultSet();) {
1089  //sort all te individual bins in to the ranges
1090  while (resultSet.next()) {
1091  final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
1092  long count = resultSet.getLong("count");
1093 
1094  BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
1095  BinResult previousResult = binRanges.get(bin);
1096 
1097  if (previousResult != null) {
1098  binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
1099  count += previousResult.getCount();
1100  }
1101 
1102  if (binRange == null) {
1103  binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
1104  } else {
1105  binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
1106  }
1107  }
1108  binRanges.asMapOfRanges().values().forEach(list::add);
1109  } catch (TskCoreException | SQLException ex) {
1110  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1111  }
1112 
1113  return true;
1114  }
1115 
1116  @Override
1117  protected Node[] createNodesForKey(BinResult key) {
1118  return new Node[]{new BINNode(key)};
1119  }
1120  }
1121 
1126  final public class ByBINNode extends DisplayableItemNode {
1127 
1131  @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
1132  private ByBINNode() {
1133  super(Children.create(new BINFactory(), true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
1134  setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
1135  updateDisplayName();
1136  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1137  reviewStatusBus.register(this);
1138  }
1139 
1140  @NbBundle.Messages({
1141  "# {0} - number of children",
1142  "Accounts.ByBINNode.displayName=By BIN ({0})"})
1143  private void updateDisplayName() {
1144  String query
1145  = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1146  + " FROM blackboard_artifacts " //NON-NLS
1147  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1148  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1149  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1151  + getRejectedArtifactFilterClause(); //NON-NLS
1152  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1153  ResultSet resultSet = results.getResultSet();) {
1154  while (resultSet.next()) {
1155  setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1156  }
1157  } catch (TskCoreException | SQLException ex) {
1158  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1159  }
1160  }
1161 
1162  @Override
1163  public boolean isLeafTypeNode() {
1164  return false;
1165  }
1166 
1167  @Override
1168  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1169  return visitor.visit(this);
1170  }
1171 
1172  @Override
1173  public String getItemType() {
1174  return getClass().getName();
1175  }
1176 
1177  @Subscribe
1178  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1179  updateDisplayName();
1180  }
1181 
1182  @Subscribe
1183  void handleDataAdded(ModuleDataEvent event) {
1184  updateDisplayName();
1185  }
1186  }
1187 
1192  @Immutable
1193  final private static class FileWithCCN {
1194 
1195  @Override
1196  public int hashCode() {
1197  int hash = 5;
1198  hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1199  hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1200  hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1201  hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1202  hash = 79 * hash + Objects.hashCode(this.statuses);
1203  return hash;
1204  }
1205 
1206  @Override
1207  public boolean equals(Object obj) {
1208  if (this == obj) {
1209  return true;
1210  }
1211  if (obj == null) {
1212  return false;
1213  }
1214  if (getClass() != obj.getClass()) {
1215  return false;
1216  }
1217  final FileWithCCN other = (FileWithCCN) obj;
1218  if (this.objID != other.objID) {
1219  return false;
1220  }
1221  if (this.hits != other.hits) {
1222  return false;
1223  }
1224  if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
1225  return false;
1226  }
1227  if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
1228  return false;
1229  }
1230  if (!Objects.equals(this.statuses, other.statuses)) {
1231  return false;
1232  }
1233  return true;
1234  }
1235 
1236  private final long objID;
1237  private final String keywordSearchDocID;
1238  private final List<Long> artifactIDs;
1239  private final long hits;
1240  private final Set<BlackboardArtifact.ReviewStatus> statuses;
1241 
1242  private FileWithCCN(long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1243  this.objID = objID;
1244  this.keywordSearchDocID = solrDocID;
1245  this.artifactIDs = artifactIDs;
1246  this.hits = hits;
1247  this.statuses = statuses;
1248  }
1249 
1255  public long getObjID() {
1256  return objID;
1257  }
1258 
1265  public String getkeywordSearchDocID() {
1266  return keywordSearchDocID;
1267  }
1268 
1274  public List<Long> getArtifactIDs() {
1275  return Collections.unmodifiableList(artifactIDs);
1276  }
1277 
1283  public long getHits() {
1284  return hits;
1285  }
1286 
1292  public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
1293  return Collections.unmodifiableSet(statuses);
1294  }
1295  }
1296 
1313  static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1314  return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1315  : Stream.of(groupConcat.split(",")) //NON-NLS
1316  .map(mapper::apply)
1317  .collect(Collectors.toList());
1318  }
1319 
1323  final public class FileWithCCNNode extends DisplayableItemNode {
1324 
1325  private final FileWithCCN fileKey;
1326  private final String fileName;
1327 
1337  @NbBundle.Messages({
1338  "# {0} - raw file name",
1339  "# {1} - solr chunk id",
1340  "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1341  private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
1342  super(Children.LEAF, Lookups.fixed(lookupContents));
1343  this.fileKey = key;
1344  this.fileName = (key.getkeywordSearchDocID() == null)
1345  ? content.getName()
1346  : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
1347  setName(fileName + key.getObjID());
1348  setDisplayName(fileName);
1349  }
1350 
1351  @Override
1352  public boolean isLeafTypeNode() {
1353  return true;
1354  }
1355 
1356  @Override
1357  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1358  return visitor.visit(this);
1359  }
1360 
1361  @Override
1362  public String getItemType() {
1363  return getClass().getName();
1364  }
1365 
1366  @Override
1367  @NbBundle.Messages({
1368  "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1369  "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1370  "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1371  "Accounts.FileWithCCNNode.noDescription=no description"})
1372  protected Sheet createSheet() {
1373  Sheet sheet = super.createSheet();
1374  Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1375  if (propSet == null) {
1376  propSet = Sheet.createPropertiesSet();
1377  sheet.put(propSet);
1378  }
1379 
1380  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1381  Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1382  Bundle.Accounts_FileWithCCNNode_noDescription(),
1383  fileName));
1384  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1385  Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1386  Bundle.Accounts_FileWithCCNNode_noDescription(),
1387  fileKey.getHits()));
1388  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1389  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1390  Bundle.Accounts_FileWithCCNNode_noDescription(),
1391  fileKey.getStatuses().stream()
1392  .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1393  .collect(Collectors.joining(", ")))); //NON-NLS
1394 
1395  return sheet;
1396  }
1397 
1398  @Override
1399  public Action[] getActions(boolean context) {
1400  Action[] actions = super.getActions(context);
1401  ArrayList<Action> arrayList = new ArrayList<>();
1402  arrayList.addAll(Arrays.asList(actions));
1403  try {
1404  arrayList.addAll(DataModelActionsFactory.getActions(Accounts.this.skCase.getContentById(fileKey.getObjID()), false));
1405  } catch (TskCoreException ex) {
1406  LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1407  }
1408 
1409  arrayList.add(approveActionInstance);
1410  arrayList.add(rejectActionInstance);
1411 
1412  return arrayList.toArray(new Action[arrayList.size()]);
1413  }
1414  }
1415 
1416  final private class CreditCardNumberFactory extends ObservingChildren<Long> {
1417 
1418  private final BinResult bin;
1419 
1421  this.bin = bin;
1422  }
1423 
1424  @Subscribe
1425  @Override
1426  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1427  refresh(true);
1428  }
1429 
1430  @Subscribe
1431  @Override
1432  void handleDataAdded(ModuleDataEvent event) {
1433  refresh(true);
1434  }
1435 
1436  @Override
1437  protected boolean createKeys(List<Long> list) {
1438 
1439  String query
1440  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1441  + " FROM blackboard_artifacts " //NON-NLS
1442  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1443  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1444  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1445  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1448  + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1449  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1450  ResultSet rs = results.getResultSet();) {
1451  while (rs.next()) {
1452  list.add(rs.getLong("artifact_id")); //NON-NLS
1453  }
1454  } catch (TskCoreException | SQLException ex) {
1455  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1456 
1457  }
1458  return true;
1459  }
1460 
1461  @Override
1462  protected Node[] createNodesForKey(Long artifactID) {
1463  if (skCase == null) {
1464  return new Node[0];
1465  }
1466 
1467  try {
1468  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1469  return new Node[]{new AccountArtifactNode(art)};
1470  } catch (TskCoreException ex) {
1471  LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1472  return new Node[0];
1473  }
1474  }
1475  }
1476 
1477  private String getBinRangeString(BinResult bin) {
1478  if (bin.getBINStart() == bin.getBINEnd()) {
1479  return Integer.toString(bin.getBINStart());
1480  } else {
1481  return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1482  }
1483  }
1484 
1485  final public class BINNode extends DisplayableItemNode {
1486 
1490  private final BinResult bin;
1491 
1492  private BINNode(BinResult bin) {
1493  super(Children.create(new CreditCardNumberFactory(bin), true), Lookups.singleton(getBinRangeString(bin)));
1494  this.bin = bin;
1495  setName(getBinRangeString(bin));
1496  updateDisplayName();
1497  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1498  reviewStatusBus.register(this);
1499  }
1500 
1501  @Subscribe
1502  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1503  updateDisplayName();
1504  updateSheet();
1505  }
1506 
1507  @Subscribe
1508  void handleDataAdded(ModuleDataEvent event) {
1509  updateDisplayName();
1510  }
1511 
1512  private void updateDisplayName() {
1513  String query
1514  = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1515  + " FROM blackboard_artifacts " //NON-NLS
1516  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1517  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1518  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1519  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1522  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1523  ResultSet resultSet = results.getResultSet();) {
1524  while (resultSet.next()) {
1525  setDisplayName(getBinRangeString(bin) + " (" + resultSet.getLong("count") + ")"); //NON-NLS
1526  }
1527  } catch (TskCoreException | SQLException ex) {
1528  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1529 
1530  }
1531 
1532  }
1533 
1534  @Override
1535  public boolean isLeafTypeNode() {
1536  return true;
1537  }
1538 
1539  @Override
1540  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1541  return visitor.visit(this);
1542  }
1543 
1544  @Override
1545  public String getItemType() {
1546  return getClass().getName();
1547  }
1548 
1549  private Sheet.Set getPropertySet(Sheet sheet) {
1550  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1551  if (sheetSet == null) {
1552  sheetSet = Sheet.createPropertiesSet();
1553  sheet.put(sheetSet);
1554  }
1555  return sheetSet;
1556  }
1557 
1558  @Override
1559  @NbBundle.Messages({
1560  "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1561  "Accounts.BINNode.accountsProperty.displayName=Accounts",
1562  "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1563  "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1564  "Accounts.BINNode.brandProperty.displayName=Brand",
1565  "Accounts.BINNode.bankProperty.displayName=Bank",
1566  "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1567  "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1568  "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1569  "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1570  "Accounts.BINNode.noDescription=no description"})
1571  protected Sheet createSheet() {
1572  Sheet sheet = super.createSheet();
1573  Sheet.Set properties = getPropertySet(sheet);
1574 
1575  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1576  Bundle.Accounts_BINNode_binProperty_displayName(),
1577  Bundle.Accounts_BINNode_noDescription(),
1578  getBinRangeString(bin)));
1579  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1580  Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1581  bin.getCount()));
1582 
1583  //add optional properties if they are available
1584  if (bin.hasDetails()) {
1585  bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1586  Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1587  cardType)));
1588  bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1589  Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1590  scheme)));
1591  bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1592  Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1593  brand)));
1594  bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1595  Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1596  bankName)));
1597  bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1598  Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1599  bankCity)));
1600  bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1601  Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1602  country)));
1603  bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1604  Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1605  phoneNumber)));
1606  bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1607  Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1608  url)));
1609  }
1610  return sheet;
1611  }
1612 
1613  private void updateSheet() {
1614  this.setSheet(createSheet());
1615  }
1616 
1617  }
1618 
1623  @Immutable
1624  final static private class BinResult implements CreditCards.BankIdentificationNumber {
1625 
1626  @Override
1627  public int hashCode() {
1628  int hash = 3;
1629  hash = 97 * hash + this.binEnd;
1630  hash = 97 * hash + this.binStart;
1631  return hash;
1632  }
1633 
1634  @Override
1635  public boolean equals(Object obj) {
1636  if (this == obj) {
1637  return true;
1638  }
1639  if (obj == null) {
1640  return false;
1641  }
1642  if (getClass() != obj.getClass()) {
1643  return false;
1644  }
1645  final BinResult other = (BinResult) obj;
1646  if (this.binEnd != other.binEnd) {
1647  return false;
1648  }
1649  if (this.binStart != other.binStart) {
1650  return false;
1651  }
1652  return true;
1653  }
1654 
1658  private final long count;
1659 
1660  private final BINRange binRange;
1661  private final int binEnd;
1662  private final int binStart;
1663 
1664  private BinResult(long count, @Nonnull BINRange binRange) {
1665  this.count = count;
1666  this.binRange = binRange;
1667  binStart = binRange.getBINstart();
1668  binEnd = binRange.getBINend();
1669  }
1670 
1671  private BinResult(long count, int start, int end) {
1672  this.count = count;
1673  this.binRange = null;
1674  binStart = start;
1675  binEnd = end;
1676  }
1677 
1678  int getBINStart() {
1679  return binStart;
1680  }
1681 
1682  int getBINEnd() {
1683  return binEnd;
1684  }
1685 
1686  long getCount() {
1687  return count;
1688  }
1689 
1690  boolean hasDetails() {
1691  return binRange != null;
1692  }
1693 
1694  @Override
1695  public Optional<Integer> getNumberLength() {
1696  return binRange.getNumberLength();
1697  }
1698 
1699  @Override
1700  public Optional<String> getBankCity() {
1701  return binRange.getBankCity();
1702  }
1703 
1704  @Override
1705  public Optional<String> getBankName() {
1706  return binRange.getBankName();
1707  }
1708 
1709  @Override
1710  public Optional<String> getBankPhoneNumber() {
1711  return binRange.getBankPhoneNumber();
1712  }
1713 
1714  @Override
1715  public Optional<String> getBankURL() {
1716  return binRange.getBankURL();
1717  }
1718 
1719  @Override
1720  public Optional<String> getBrand() {
1721  return binRange.getBrand();
1722  }
1723 
1724  @Override
1725  public Optional<String> getCardType() {
1726  return binRange.getCardType();
1727  }
1728 
1729  @Override
1730  public Optional<String> getCountry() {
1731  return binRange.getCountry();
1732  }
1733 
1734  @Override
1735  public Optional<String> getScheme() {
1736  return binRange.getScheme();
1737  }
1738  }
1739 
1740  final private class AccountArtifactNode extends BlackboardArtifactNode {
1741 
1742  private final BlackboardArtifact artifact;
1743 
1744  private AccountArtifactNode(BlackboardArtifact artifact) {
1745  super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1746  this.artifact = artifact;
1747  setName(Long.toString(this.artifact.getArtifactID()));
1748 
1749  reviewStatusBus.register(this);
1750  }
1751 
1752  @Override
1753  public Action[] getActions(boolean context) {
1754  List<Action> actionsList = new ArrayList<>();
1755  actionsList.addAll(Arrays.asList(super.getActions(context)));
1756 
1757  actionsList.add(approveActionInstance);
1758  actionsList.add(rejectActionInstance);
1759 
1760  return actionsList.toArray(new Action[actionsList.size()]);
1761  }
1762 
1763  @Override
1764  protected Sheet createSheet() {
1765  Sheet sheet = super.createSheet();
1766  Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1767  if (properties == null) {
1768  properties = Sheet.createPropertiesSet();
1769  sheet.put(properties);
1770  }
1771  properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1772  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1773  Bundle.Accounts_FileWithCCNNode_noDescription(),
1774  artifact.getReviewStatus().getDisplayName()));
1775 
1776  return sheet;
1777  }
1778 
1779  @Subscribe
1780  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1781 
1782  // Update the node if event includes this artifact
1783  event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1784  return _item;
1785  }).forEachOrdered((_item) -> {
1786  updateSheet();
1787  });
1788  }
1789 
1790  private void updateSheet() {
1791  this.setSheet(createSheet());
1792  }
1793 
1794  }
1795 
1796  @Deprecated
1797  private final class ToggleShowRejected extends AbstractAction {
1798 
1799  @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1800  ToggleShowRejected() {
1801  super(Bundle.ToggleShowRejected_name());
1802  }
1803 
1804  @Override
1805  public void actionPerformed(ActionEvent e) {
1806  showRejected = !showRejected;
1807  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1808  }
1809  }
1810 
1816  public void setShowRejected(boolean showRejected) {
1817  this.showRejected = showRejected;
1818  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1819  }
1820 
1821  private abstract class ReviewStatusAction extends AbstractAction {
1822 
1823  private final BlackboardArtifact.ReviewStatus newStatus;
1824 
1825  private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1826  super(displayName);
1827  this.newStatus = newStatus;
1828 
1829  }
1830 
1831  @Override
1832  public void actionPerformed(ActionEvent e) {
1833 
1834  /*
1835  * get paths for selected nodes to reselect after applying review
1836  * status change
1837  */
1838  List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1839  .map(node -> {
1840  String[] createPath;
1841  /*
1842  * If the we are rejecting and not showing rejected
1843  * results, then the selected node, won't exist any
1844  * more, so we select the previous one in stead.
1845  */
1846  if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1847  List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1848  if (siblings.size() > 1) {
1849  int indexOf = siblings.indexOf(node);
1850  //there is no previous for the first node, so instead we select the next one
1851  Node sibling = indexOf > 0
1852  ? siblings.get(indexOf - 1)
1853  : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1854  createPath = NodeOp.createPath(sibling, null);
1855  } else {
1856  /*
1857  * if there are no other siblings to select,
1858  * just return null, but note we need to filter
1859  * this out of stream below
1860  */
1861  return null;
1862  }
1863  } else {
1864  createPath = NodeOp.createPath(node, null);
1865  }
1866  //for the reselect to work we need to strip off the first part of the path.
1867  return Arrays.copyOfRange(createPath, 1, createPath.length);
1868  })
1869  .filter(Objects::nonNull)
1870  .collect(Collectors.toList());
1871 
1872  //change status of selected artifacts
1873  final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1874  artifacts.forEach(artifact -> {
1875  try {
1876  artifact.setReviewStatus(newStatus);
1877  } catch (TskCoreException ex) {
1878  LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1879  }
1880  });
1881  //post event
1882  reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1883 
1884  final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1885  final Node rootNode = directoryListing.getRootNode();
1886 
1887  //convert paths back to nodes
1888  List<Node> toArray = new ArrayList<>();
1889  selectedPaths.forEach(path -> {
1890  try {
1891  toArray.add(NodeOp.findPath(rootNode, path));
1892  } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1893  //just ingnore paths taht don't exist. this is expected since we are rejecting
1894  }
1895  });
1896  //select nodes
1897  directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1898  }
1899  }
1900 
1901  final private class ApproveAccounts extends ReviewStatusAction {
1902 
1903  @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1904  private ApproveAccounts() {
1905  super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1906  }
1907  }
1908 
1909  final private class RejectAccounts extends ReviewStatusAction {
1910 
1911  @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1912  private RejectAccounts() {
1913  super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1914  }
1915  }
1916 
1917  static private class ReviewStatusChangeEvent {
1918 
1919  Collection<? extends BlackboardArtifact> artifacts;
1920  BlackboardArtifact.ReviewStatus newReviewStatus;
1921 
1922  ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1923  this.artifacts = artifacts;
1924  this.newReviewStatus = newReviewStatus;
1925  }
1926  }
1927 
1933  public static String getIconFilePath(Account.Type type) {
1934 
1935  if (type.equals(Account.Type.CREDIT_CARD)) {
1936  return ICON_BASE_PATH + "credit-card.png";
1937  } else if (type.equals(Account.Type.DEVICE)) {
1938  return ICON_BASE_PATH + "image.png";
1939  } else if (type.equals(Account.Type.EMAIL)) {
1940  return ICON_BASE_PATH + "email.png";
1941  } else if (type.equals(Account.Type.FACEBOOK)) {
1942  return ICON_BASE_PATH + "facebook.png";
1943  } else if (type.equals(Account.Type.INSTAGRAM)) {
1944  return ICON_BASE_PATH + "instagram.png";
1945  } else if (type.equals(Account.Type.MESSAGING_APP)) {
1946  return ICON_BASE_PATH + "messaging.png";
1947  } else if (type.equals(Account.Type.PHONE)) {
1948  return ICON_BASE_PATH + "phone.png";
1949  } else if (type.equals(Account.Type.TWITTER)) {
1950  return ICON_BASE_PATH + "twitter.png";
1951  } else if (type.equals(Account.Type.WEBSITE)) {
1952  return ICON_BASE_PATH + "web-file.png";
1953  } else if (type.equals(Account.Type.WHATSAPP)) {
1954  return ICON_BASE_PATH + "WhatsApp.png";
1955  } else {
1956  //there could be a default icon instead...
1957  return ICON_BASE_PATH + "face.png";
1958 // throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
1959  }
1960  }
1961 }
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
Definition: Accounts.java:95
boolean createKeys(List< CreditCardViewMode > list)
Definition: Accounts.java:708
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
Definition: Accounts.java:1292
static List< Action > getActions(File file, boolean isArtifactSource)
static synchronized BankIdentificationNumber getBINInfo(int bin)
static String getIconFilePath(Account.Type type)
Definition: Accounts.java:1933
void removeIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.ReviewStatus > statuses
Definition: Accounts.java:1240
void addIngestJobEventListener(final PropertyChangeListener listener)
BinResult(long count,@Nonnull BINRange binRange)
Definition: Accounts.java:1664
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Definition: Accounts.java:1341
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
Definition: Accounts.java:1242
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:1825
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:536

Copyright © 2012-2020 Basis Technology. Generated on: Mon Jul 6 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.