Autopsy  4.11.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-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datamodel.accounts;
20 
21 import com.google.common.collect.Range;
22 import com.google.common.collect.RangeMap;
23 import com.google.common.collect.TreeRangeMap;
24 import com.google.common.eventbus.EventBus;
25 import com.google.common.eventbus.Subscribe;
26 import java.awt.event.ActionEvent;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.EnumSet;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.Optional;
40 import java.util.Set;
41 import java.util.function.Function;
42 import java.util.logging.Level;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
45 import javax.annotation.Nonnull;
46 import javax.annotation.concurrent.Immutable;
47 import javax.swing.AbstractAction;
48 import javax.swing.Action;
49 import org.apache.commons.lang3.StringUtils;
50 import org.openide.nodes.ChildFactory;
51 import org.openide.nodes.Children;
52 import org.openide.nodes.Node;
53 import org.openide.nodes.NodeNotFoundException;
54 import org.openide.nodes.NodeOp;
55 import org.openide.nodes.Sheet;
56 import org.openide.util.NbBundle;
57 import org.openide.util.Utilities;
58 import org.openide.util.lookup.Lookups;
74 import org.sleuthkit.datamodel.AbstractFile;
75 import org.sleuthkit.datamodel.Account;
76 import org.sleuthkit.datamodel.BlackboardArtifact;
77 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
78 import org.sleuthkit.datamodel.BlackboardAttribute;
79 import org.sleuthkit.datamodel.Content;
80 import org.sleuthkit.datamodel.SleuthkitCase;
81 import org.sleuthkit.datamodel.TskCoreException;
82 import org.sleuthkit.datamodel.TskData.DbType;
83 
88 final public class Accounts implements AutopsyVisitableItem {
89 
90  private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
91  private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
92 
93  @NbBundle.Messages("AccountsRootNode.name=Accounts")
94  final public static String NAME = Bundle.AccountsRootNode_name();
95 
96  private SleuthkitCase skCase;
97  private final long filteringDSObjId; // 0 if not filtering/grouping by data source
98 
99  private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
100 
101  /* Should rejected accounts be shown in the accounts section of the tree. */
102  private boolean showRejected = false; //NOPMD redundant initializer
103 
106 
112  public Accounts(SleuthkitCase skCase) {
113  this(skCase, 0);
114  }
115 
122  public Accounts(SleuthkitCase skCase, long objId) {
123  this.skCase = skCase;
124  this.filteringDSObjId = objId;
125 
126  this.rejectActionInstance = new RejectAccounts();
127  this.approveActionInstance = new ApproveAccounts();
128  }
129 
130 
131  @Override
132  public <T> T accept(AutopsyItemVisitor<T> visitor) {
133  return visitor.visit(this);
134  }
135 
144  return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
145  }
146 
153  private String getFilterByDataSourceClause() {
154  if (filteringDSObjId > 0) {
155  return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
156  }
157 
158  return " ";
159  }
160 
168  @Deprecated
169  public Action newToggleShowRejectedAction() {
170  return new ToggleShowRejected();
171  }
172 
179  private abstract class ObservingChildren<X> extends ChildFactory.Detachable<X> {
180 
186  super();
187  }
188 
193  abstract protected boolean createKeys(List<X> list);
194 
200  @Subscribe
201  abstract void handleReviewStatusChange(ReviewStatusChangeEvent event);
202 
203  @Subscribe
204  abstract void handleDataAdded(ModuleDataEvent event);
205 
206  @Override
207  protected void removeNotify() {
208  super.removeNotify();
209  reviewStatusBus.unregister(ObservingChildren.this);
210  }
211 
212  @Override
213  protected void addNotify() {
214  super.addNotify();
215  refresh(true);
216  reviewStatusBus.register(ObservingChildren.this);
217  }
218  }
219 
223  @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
224  final public class AccountsRootNode extends DisplayableItemNode {
225 
226  public AccountsRootNode() {
227  super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
228  setName(Accounts.NAME);
229  setDisplayName(Bundle.Accounts_RootNode_displayName());
230  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
231  }
232 
233  @Override
234  public boolean isLeafTypeNode() {
235  return false;
236  }
237 
238  @Override
239  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
240  return visitor.visit(this);
241  }
242 
243  @Override
244  public String getItemType() {
245  return getClass().getName();
246  }
247  }
248 
252  private class AccountTypeFactory extends ObservingChildren<String> {
253 
254  /*
255  * The pcl is in this class because it has the easiest mechanisms to add
256  * and remove itself during its life cycles.
257  */
258  private final PropertyChangeListener pcl = new PropertyChangeListener() {
259  @Override
260  public void propertyChange(PropertyChangeEvent evt) {
261  String eventType = evt.getPropertyName();
262  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
269  try {
277  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
278  if (null != eventData
279  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
280  reviewStatusBus.post(eventData);
281  }
282  } catch (NoCurrentCaseException notUsed) {
283  // Case is closed, do nothing.
284  }
285  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
286  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
293  try {
295  refresh(true);
296  } catch (NoCurrentCaseException notUsed) {
297  // Case is closed, do nothing.
298  }
299  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
300  // case was closed. Remove listeners so that we don't get called with a stale case handle
301  if (evt.getNewValue() == null) {
302  removeNotify();
303  skCase = null;
304  }
305  }
306  }
307  };
308 
309  @Subscribe
310  @Override
311  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
312  refresh(true);
313  }
314 
315  @Subscribe
316  @Override
317  void handleDataAdded(ModuleDataEvent event) {
318  refresh(true);
319  }
320 
321  @Override
322  protected boolean createKeys(List<String> list) {
323  String accountTypesInUseQuery =
324  "SELECT DISTINCT blackboard_attributes.value_text as account_type "
325  + " FROM blackboard_artifacts " //NON-NLS
326  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
327  + " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
329 
330  try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery );
331  ResultSet resultSet = executeQuery.getResultSet()) {
332  while (resultSet.next()) {
333  String accountType = resultSet.getString("account_type");
334  list.add(accountType);
335  }
336  } catch (TskCoreException | SQLException ex) {
337  LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
338  }
339 
340  return true;
341  }
342 
343  @Override
344  protected Node[] createNodesForKey(String acountTypeName) {
345 
346  if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) {
347  return new Node[]{new CreditCardNumberAccountTypeNode()};
348  } else {
349 
350  try {
351  Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName);
352  return new Node[]{new DefaultAccountTypeNode(accountType)};
353  } catch (TskCoreException ex) {
354  LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
355  }
356 
357  return new Node[]{};
358  }
359  }
360 
361  @Override
362  protected void removeNotify() {
366  super.removeNotify();
367  }
368 
369  @Override
370  protected void addNotify() {
374  super.addNotify();
375  refresh(true);
376  }
377 
378  }
379 
380  final private class DefaultAccountFactory extends ObservingChildren<Long> {
381 
382  private final Account.Type accountType;
383 
384  private DefaultAccountFactory(Account.Type accountType) {
385  this.accountType = accountType;
386  }
387 
388  private final PropertyChangeListener pcl = new PropertyChangeListener() {
389  @Override
390  public void propertyChange(PropertyChangeEvent evt) {
391  String eventType = evt.getPropertyName();
392  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
399  try {
407  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
408  if (null != eventData
409  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
410  reviewStatusBus.post(eventData);
411  }
412  } catch (NoCurrentCaseException notUsed) {
413  // Case is closed, do nothing.
414  }
415  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
416  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
423  try {
425  refresh(true);
426 
427  } catch (NoCurrentCaseException notUsed) {
428  // Case is closed, do nothing.
429  }
430  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
431  // case was closed. Remove listeners so that we don't get called with a stale case handle
432  if (evt.getNewValue() == null) {
433  removeNotify();
434  skCase = null;
435  }
436  }
437  }
438  };
439 
440  @Override
441  protected void addNotify() {
445  super.addNotify();
446  }
447 
448  @Override
449  protected void removeNotify() {
453  super.removeNotify();
454  }
455 
456  @Override
457  protected boolean createKeys(List<Long> list) {
458  String query =
459  "SELECT blackboard_artifacts.artifact_id " //NON-NLS
460  + " FROM blackboard_artifacts " //NON-NLS
461  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
462  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
463  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
464  + " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
466  + getRejectedArtifactFilterClause(); //NON-NLS
467  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
468  ResultSet rs = results.getResultSet();) {
469  while (rs.next()) {
470  list.add(rs.getLong("artifact_id")); //NON-NLS
471  }
472  } catch (TskCoreException | SQLException ex) {
473  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
474  }
475 
476  return true;
477  }
478 
479  @Override
480  protected Node[] createNodesForKey(Long t) {
481  try {
482  return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))};
483  } catch (TskCoreException ex) {
484  LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
485  return new Node[0];
486  }
487  }
488 
489  @Subscribe
490  @Override
491  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
492  refresh(true);
493  }
494 
495  @Subscribe
496  @Override
497  void handleDataAdded(ModuleDataEvent event) {
498  refresh(true);
499  }
500  }
501 
506  final public class DefaultAccountTypeNode extends DisplayableItemNode {
507 
508  private DefaultAccountTypeNode(Account.Type accountType) {
509  super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
510  setName(accountType.getDisplayName());
511  this.setIconBaseWithExtension(getIconFilePath(accountType)); //NON-NLS
512  }
513 
514  @Override
515  public boolean isLeafTypeNode() {
516  return true;
517  }
518 
519  @Override
520  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
521  return visitor.visit(this);
522  }
523 
524  @Override
525  public String getItemType() {
526  return getClass().getName();
527  }
528  }
529 
533  private enum CreditCardViewMode {
536  }
537 
538  final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
539 
540  private final PropertyChangeListener pcl = new PropertyChangeListener() {
541  @Override
542  public void propertyChange(PropertyChangeEvent evt) {
543  String eventType = evt.getPropertyName();
544  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
551  try {
559  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
560  if (null != eventData
561  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
562  reviewStatusBus.post(eventData);
563  }
564  } catch (NoCurrentCaseException notUsed) {
565  // Case is closed, do nothing.
566  }
567  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
568  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
575  try {
577  refresh(true);
578 
579  } catch (NoCurrentCaseException notUsed) {
580  // Case is closed, do nothing.
581  }
582  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
583  // case was closed. Remove listeners so that we don't get called with a stale case handle
584  if (evt.getNewValue() == null) {
585  removeNotify();
586  skCase = null;
587  }
588  }
589  }
590  };
591 
592  @Subscribe
593  @Override
594  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
595  refresh(true);
596  }
597 
598  @Subscribe
599  @Override
600  void handleDataAdded(ModuleDataEvent event) {
601  refresh(true);
602  }
603 
604  @Override
605  protected void addNotify() {
609  super.addNotify();
610  }
611 
612  @Override
613  protected void removeNotify() {
617  super.removeNotify();
618  }
619 
623  @Override
624  protected boolean createKeys(List<CreditCardViewMode> list) {
625  list.addAll(Arrays.asList(CreditCardViewMode.values()));
626 
627  return true;
628  }
629 
630  @Override
631  protected Node[] createNodesForKey(CreditCardViewMode key) {
632  switch (key) {
633  case BY_BIN:
634  return new Node[]{new ByBINNode()};
635  case BY_FILE:
636  return new Node[]{new ByFileNode()};
637  default:
638  return new Node[0];
639  }
640  }
641  }
642 
647 
653  super(Children.create(new ViewModeFactory(), true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
654  setName(Account.Type.CREDIT_CARD.getDisplayName());
655  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
656  }
657 
658  @Override
659  public boolean isLeafTypeNode() {
660  return false;
661  }
662 
663  @Override
664  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
665  return visitor.visit(this);
666  }
667 
668  @Override
669  public String getItemType() {
670  return getClass().getName();
671  }
672  }
673 
674  final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
675 
676  private final PropertyChangeListener pcl = new PropertyChangeListener() {
677  @Override
678  public void propertyChange(PropertyChangeEvent evt) {
679  String eventType = evt.getPropertyName();
680  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
687  try {
695  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
696  if (null != eventData
697  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
698  reviewStatusBus.post(eventData);
699  }
700  } catch (NoCurrentCaseException notUsed) {
701  // Case is closed, do nothing.
702  }
703  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
704  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
711  try {
713  refresh(true);
714 
715  } catch (NoCurrentCaseException notUsed) {
716  // Case is closed, do nothing.
717  }
718  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
719  // case was closed. Remove listeners so that we don't get called with a stale case handle
720  if (evt.getNewValue() == null) {
721  removeNotify();
722  skCase = null;
723  }
724  }
725  }
726  };
727 
728  @Override
729  protected void addNotify() {
733  super.addNotify();
734  }
735 
736  @Override
737  protected void removeNotify() {
741  super.removeNotify();
742  }
743 
744  @Subscribe
745  @Override
746  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
747  refresh(true);
748  }
749 
750  @Subscribe
751  @Override
752  void handleDataAdded(ModuleDataEvent event) {
753  refresh(true);
754  }
755 
756  @Override
757  protected boolean createKeys(List<FileWithCCN> list) {
758  String query =
759  "SELECT blackboard_artifacts.obj_id," //NON-NLS
760  + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
761  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
762  query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
763  + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
764  } else {
765  query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
766  + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
767  }
768  query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
769  + " FROM blackboard_artifacts " //NON-NLS
770  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
771  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
772  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
773  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
774  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
775  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
778  + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
779  + " ORDER BY hits DESC "; //NON-NLS
780  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
781  ResultSet resultSet = results.getResultSet();) {
782  while (resultSet.next()) {
783  list.add(new FileWithCCN(
784  resultSet.getLong("obj_id"), //NON-NLS
785  resultSet.getString("solr_document_id"), //NON-NLS
786  unGroupConcat(resultSet.getString("artifact_IDs"), Long::valueOf), //NON-NLS
787  resultSet.getLong("hits"), //NON-NLS
788  new HashSet<>(unGroupConcat(resultSet.getString("review_status_ids"), reviewStatusID -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(reviewStatusID)))))); //NON-NLS
789  }
790  } catch (TskCoreException | SQLException ex) {
791  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
792 
793  }
794  return true;
795  }
796 
797  @Override
798  protected Node[] createNodesForKey(FileWithCCN key) {
799  //add all account artifacts for the file and the file itself to the lookup
800  try {
801  List<Object> lookupContents = new ArrayList<>();
802  for (long artId : key.artifactIDs) {
803  lookupContents.add(skCase.getBlackboardArtifact(artId));
804  }
805  AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID());
806  lookupContents.add(abstractFileById);
807  return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
808  } catch (TskCoreException ex) {
809  LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
810  return new Node[0];
811  }
812  }
813  }
814 
819  final public class ByFileNode extends DisplayableItemNode {
820 
824  private ByFileNode() {
825  super(Children.create(new FileWithCCNFactory(), true), Lookups.singleton("By File"));
826  setName("By File"); //NON-NLS
827  updateDisplayName();
828  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
829  reviewStatusBus.register(this);
830  }
831 
832  @NbBundle.Messages({
833  "# {0} - number of children",
834  "Accounts.ByFileNode.displayName=By File ({0})"})
835  private void updateDisplayName() {
836  String query =
837  "SELECT count(*) FROM ( SELECT count(*) AS documents "
838  + " FROM blackboard_artifacts " //NON-NLS
839  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
840  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
841  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
842  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
843  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
844  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
847  + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
848  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
849  ResultSet resultSet = results.getResultSet();) {
850  while (resultSet.next()) {
851  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
852  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count")));
853  } else {
854  setDisplayName(Bundle.Accounts_ByFileNode_displayName(resultSet.getLong("count(*)")));
855  }
856  }
857  } catch (TskCoreException | SQLException ex) {
858  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
859 
860  }
861  }
862 
863  @Override
864  public boolean isLeafTypeNode() {
865  return true;
866  }
867 
868  @Override
869  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
870  return visitor.visit(this);
871  }
872 
873  @Override
874  public String getItemType() {
875  return getClass().getName();
876  }
877 
878  @Subscribe
879  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
880  updateDisplayName();
881  }
882 
883  @Subscribe
884  void handleDataAdded(ModuleDataEvent event) {
885  updateDisplayName();
886  }
887  }
888 
889  final private class BINFactory extends ObservingChildren<BinResult> {
890 
891  private final PropertyChangeListener pcl = new PropertyChangeListener() {
892  @Override
893  public void propertyChange(PropertyChangeEvent evt) {
894  String eventType = evt.getPropertyName();
895  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
902  try {
910  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
911  if (null != eventData
912  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
913  reviewStatusBus.post(eventData);
914  }
915  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
916  // Case is closed, do nothing.
917  }
918  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
919  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
926  try {
928 
929  refresh(true);
930  } catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
931  // Case is closed, do nothing.
932  }
933  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
934  && (evt.getNewValue() == null)) {
935  // case was closed. Remove listeners so that we don't get called with a stale case handle
936  removeNotify();
937  skCase = null;
938  }
939  }
940  };
941 
942  @Override
943  protected void addNotify() {
947  super.addNotify();
948  }
949 
950  @Override
951  protected void removeNotify() {
955  super.removeNotify();
956  }
957 
958  @Subscribe
959  @Override
960  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
961  refresh(true);
962  }
963 
964  @Subscribe
965  @Override
966  void handleDataAdded(ModuleDataEvent event) {
967  refresh(true);
968  }
969 
970  @Override
971  protected boolean createKeys(List<BinResult> list) {
972 
973  RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
974 
975  String query =
976  "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
977  + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
978  + " FROM blackboard_artifacts " //NON-NLS
979  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
980  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
981  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
984  + " GROUP BY BIN " //NON-NLS
985  + " ORDER BY BIN "; //NON-NLS
986  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
987  ResultSet resultSet = results.getResultSet();) {
988  //sort all te individual bins in to the ranges
989  while (resultSet.next()) {
990  final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
991  long count = resultSet.getLong("count");
992 
993  BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
994  BinResult previousResult = binRanges.get(bin);
995 
996  if (previousResult != null) {
997  binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
998  count += previousResult.getCount();
999  }
1000 
1001  if (binRange == null) {
1002  binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
1003  } else {
1004  binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
1005  }
1006  }
1007  binRanges.asMapOfRanges().values().forEach(list::add);
1008  } catch (TskCoreException | SQLException ex) {
1009  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1010  }
1011 
1012  return true;
1013  }
1014 
1015  @Override
1016  protected Node[] createNodesForKey(BinResult key) {
1017  return new Node[]{new BINNode(key)};
1018  }
1019  }
1020 
1025  final public class ByBINNode extends DisplayableItemNode {
1026 
1030  @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
1031  private ByBINNode() {
1032  super(Children.create(new BINFactory(), true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
1033  setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
1034  updateDisplayName();
1035  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1036  reviewStatusBus.register(this);
1037  }
1038 
1039  @NbBundle.Messages({
1040  "# {0} - number of children",
1041  "Accounts.ByBINNode.displayName=By BIN ({0})"})
1042  private void updateDisplayName() {
1043  String query =
1044  "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1045  + " FROM blackboard_artifacts " //NON-NLS
1046  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1047  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1048  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1050  + getRejectedArtifactFilterClause(); //NON-NLS
1051  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1052  ResultSet resultSet = results.getResultSet();) {
1053  while (resultSet.next()) {
1054  setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1055  }
1056  } catch (TskCoreException | SQLException ex) {
1057  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1058  }
1059  }
1060 
1061  @Override
1062  public boolean isLeafTypeNode() {
1063  return false;
1064  }
1065 
1066  @Override
1067  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1068  return visitor.visit(this);
1069  }
1070 
1071  @Override
1072  public String getItemType() {
1073  return getClass().getName();
1074  }
1075 
1076  @Subscribe
1077  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1078  updateDisplayName();
1079  }
1080 
1081  @Subscribe
1082  void handleDataAdded(ModuleDataEvent event) {
1083  updateDisplayName();
1084  }
1085  }
1086 
1091  @Immutable
1092  final private static class FileWithCCN {
1093 
1094  @Override
1095  public int hashCode() {
1096  int hash = 5;
1097  hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1098  hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1099  hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1100  hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1101  hash = 79 * hash + Objects.hashCode(this.statuses);
1102  return hash;
1103  }
1104 
1105  @Override
1106  public boolean equals(Object obj) {
1107  if (this == obj) {
1108  return true;
1109  }
1110  if (obj == null) {
1111  return false;
1112  }
1113  if (getClass() != obj.getClass()) {
1114  return false;
1115  }
1116  final FileWithCCN other = (FileWithCCN) obj;
1117  if (this.objID != other.objID) {
1118  return false;
1119  }
1120  if (this.hits != other.hits) {
1121  return false;
1122  }
1123  if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
1124  return false;
1125  }
1126  if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
1127  return false;
1128  }
1129  if (!Objects.equals(this.statuses, other.statuses)) {
1130  return false;
1131  }
1132  return true;
1133  }
1134 
1135  private final long objID;
1136  private final String keywordSearchDocID;
1137  private final List<Long> artifactIDs;
1138  private final long hits;
1139  private final Set<BlackboardArtifact.ReviewStatus> statuses;
1140 
1141  private FileWithCCN(long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1142  this.objID = objID;
1143  this.keywordSearchDocID = solrDocID;
1144  this.artifactIDs = artifactIDs;
1145  this.hits = hits;
1146  this.statuses = statuses;
1147  }
1148 
1154  public long getObjID() {
1155  return objID;
1156  }
1157 
1164  public String getkeywordSearchDocID() {
1165  return keywordSearchDocID;
1166  }
1167 
1173  public List<Long> getArtifactIDs() {
1174  return artifactIDs;
1175  }
1176 
1182  public long getHits() {
1183  return hits;
1184  }
1185 
1191  public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
1192  return statuses;
1193  }
1194  }
1195 
1212  static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1213  return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1214  : Stream.of(groupConcat.split(",")) //NON-NLS
1215  .map(mapper::apply)
1216  .collect(Collectors.toList());
1217  }
1218 
1222  final public class FileWithCCNNode extends DisplayableItemNode {
1223 
1224  private final FileWithCCN fileKey;
1225  private final String fileName;
1226 
1236  @NbBundle.Messages({
1237  "# {0} - raw file name",
1238  "# {1} - solr chunk id",
1239  "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1240  private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
1241  super(Children.LEAF, Lookups.fixed(lookupContents));
1242  this.fileKey = key;
1243  this.fileName = (key.getkeywordSearchDocID() == null)
1244  ? content.getName()
1245  : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
1246  setName(fileName + key.getObjID());
1247  setDisplayName(fileName);
1248  }
1249 
1250  @Override
1251  public boolean isLeafTypeNode() {
1252  return true;
1253  }
1254 
1255  @Override
1256  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1257  return visitor.visit(this);
1258  }
1259 
1260  @Override
1261  public String getItemType() {
1262  return getClass().getName();
1263  }
1264 
1265  @Override
1266  @NbBundle.Messages({
1267  "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1268  "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1269  "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1270  "Accounts.FileWithCCNNode.noDescription=no description"})
1271  protected Sheet createSheet() {
1272  Sheet sheet = super.createSheet();
1273  Sheet.Set propSet = sheet.get(Sheet.PROPERTIES);
1274  if (propSet == null) {
1275  propSet = Sheet.createPropertiesSet();
1276  sheet.put(propSet);
1277  }
1278 
1279  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1280  Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1281  Bundle.Accounts_FileWithCCNNode_noDescription(),
1282  fileName));
1283  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1284  Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1285  Bundle.Accounts_FileWithCCNNode_noDescription(),
1286  fileKey.getHits()));
1287  propSet.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1288  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1289  Bundle.Accounts_FileWithCCNNode_noDescription(),
1290  fileKey.getStatuses().stream()
1291  .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1292  .collect(Collectors.joining(", ")))); //NON-NLS
1293 
1294  return sheet;
1295  }
1296 
1297  @Override
1298  public Action[] getActions(boolean context) {
1299  Action[] actions = super.getActions(context);
1300  ArrayList<Action> arrayList = new ArrayList<>();
1301  arrayList.addAll(Arrays.asList(actions));
1302  try {
1303  arrayList.addAll(DataModelActionsFactory.getActions(Accounts.this.skCase.getContentById(fileKey.getObjID()), false));
1304  } catch (TskCoreException ex) {
1305  LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1306  }
1307 
1308  arrayList.add(approveActionInstance);
1309  arrayList.add(rejectActionInstance);
1310 
1311  return arrayList.toArray(new Action[arrayList.size()]);
1312  }
1313  }
1314 
1315  final private class CreditCardNumberFactory extends ObservingChildren<Long> {
1316 
1317  private final BinResult bin;
1318 
1320  this.bin = bin;
1321  }
1322 
1323  @Subscribe
1324  @Override
1325  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1326  refresh(true);
1327  }
1328 
1329  @Subscribe
1330  @Override
1331  void handleDataAdded(ModuleDataEvent event) {
1332  refresh(true);
1333  }
1334 
1335  @Override
1336  protected boolean createKeys(List<Long> list) {
1337 
1338  String query =
1339  "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1340  + " FROM blackboard_artifacts " //NON-NLS
1341  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1342  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1343  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1344  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1347  + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1348  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1349  ResultSet rs = results.getResultSet();) {
1350  while (rs.next()) {
1351  list.add(rs.getLong("artifact_id")); //NON-NLS
1352  }
1353  } catch (TskCoreException | SQLException ex) {
1354  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1355 
1356  }
1357  return true;
1358  }
1359 
1360  @Override
1361  protected Node[] createNodesForKey(Long artifactID) {
1362  if (skCase == null) {
1363  return new Node[0];
1364  }
1365 
1366  try {
1367  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1368  return new Node[]{new AccountArtifactNode(art)};
1369  } catch (TskCoreException ex) {
1370  LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1371  return new Node[0];
1372  }
1373  }
1374  }
1375 
1376  private String getBinRangeString(BinResult bin) {
1377  if (bin.getBINStart() == bin.getBINEnd()) {
1378  return Integer.toString(bin.getBINStart());
1379  } else {
1380  return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1381  }
1382  }
1383 
1384  final public class BINNode extends DisplayableItemNode {
1385 
1387  private final BinResult bin;
1388 
1389  private BINNode(BinResult bin) {
1390  super(Children.create(new CreditCardNumberFactory(bin), true), Lookups.singleton(getBinRangeString(bin)));
1391  this.bin = bin;
1392  setName(getBinRangeString(bin));
1393  updateDisplayName();
1394  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1395  reviewStatusBus.register(this);
1396  }
1397 
1398  @Subscribe
1399  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1400  updateDisplayName();
1401  updateSheet();
1402  }
1403 
1404  @Subscribe
1405  void handleDataAdded(ModuleDataEvent event) {
1406  updateDisplayName();
1407  }
1408 
1409  private void updateDisplayName() {
1410  String query =
1411  "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1412  + " FROM blackboard_artifacts " //NON-NLS
1413  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1414  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1415  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1416  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1419  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1420  ResultSet resultSet = results.getResultSet();) {
1421  while (resultSet.next()) {
1422  setDisplayName(getBinRangeString(bin) + " (" + resultSet.getLong("count") + ")"); //NON-NLS
1423  }
1424  } catch (TskCoreException | SQLException ex) {
1425  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1426 
1427  }
1428 
1429  }
1430 
1431  @Override
1432  public boolean isLeafTypeNode() {
1433  return true;
1434  }
1435 
1436  @Override
1437  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1438  return visitor.visit(this);
1439  }
1440 
1441  @Override
1442  public String getItemType() {
1443  return getClass().getName();
1444  }
1445 
1446  private Sheet.Set getPropertySet(Sheet sheet) {
1447  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
1448  if (sheetSet == null) {
1449  sheetSet = Sheet.createPropertiesSet();
1450  sheet.put(sheetSet);
1451  }
1452  return sheetSet;
1453  }
1454 
1455  @Override
1456  @NbBundle.Messages({
1457  "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1458  "Accounts.BINNode.accountsProperty.displayName=Accounts",
1459  "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1460  "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1461  "Accounts.BINNode.brandProperty.displayName=Brand",
1462  "Accounts.BINNode.bankProperty.displayName=Bank",
1463  "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1464  "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1465  "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1466  "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1467  "Accounts.BINNode.noDescription=no description"})
1468  protected Sheet createSheet() {
1469  Sheet sheet = super.createSheet();
1470  Sheet.Set properties = getPropertySet(sheet);
1471 
1472  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1473  Bundle.Accounts_BINNode_binProperty_displayName(),
1474  Bundle.Accounts_BINNode_noDescription(),
1475  getBinRangeString(bin)));
1476  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1477  Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1478  bin.getCount()));
1479 
1480  //add optional properties if they are available
1481  if (bin.hasDetails()) {
1482  bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1483  Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1484  cardType)));
1485  bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1486  Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1487  scheme)));
1488  bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1489  Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1490  brand)));
1491  bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1492  Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1493  bankName)));
1494  bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1495  Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1496  bankCity)));
1497  bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1498  Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1499  country)));
1500  bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1501  Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1502  phoneNumber)));
1503  bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1504  Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1505  url)));
1506  }
1507  return sheet;
1508  }
1509 
1510  private void updateSheet() {
1511  this.setSheet(createSheet());
1512  }
1513 
1514  }
1515 
1520  @Immutable
1521  final static private class BinResult implements CreditCards.BankIdentificationNumber {
1522 
1523  @Override
1524  public int hashCode() {
1525  int hash = 3;
1526  hash = 97 * hash + this.binEnd;
1527  hash = 97 * hash + this.binStart;
1528  return hash;
1529  }
1530 
1531  @Override
1532  public boolean equals(Object obj) {
1533  if (this == obj) {
1534  return true;
1535  }
1536  if (obj == null) {
1537  return false;
1538  }
1539  if (getClass() != obj.getClass()) {
1540  return false;
1541  }
1542  final BinResult other = (BinResult) obj;
1543  if (this.binEnd != other.binEnd) {
1544  return false;
1545  }
1546  if (this.binStart != other.binStart) {
1547  return false;
1548  }
1549  return true;
1550  }
1551 
1553  private final long count;
1554 
1555  private final BINRange binRange;
1556  private final int binEnd;
1557  private final int binStart;
1558 
1559  private BinResult(long count, @Nonnull BINRange binRange) {
1560  this.count = count;
1561  this.binRange = binRange;
1562  binStart = binRange.getBINstart();
1563  binEnd = binRange.getBINend();
1564  }
1565 
1566  private BinResult(long count, int start, int end) {
1567  this.count = count;
1568  this.binRange = null;
1569  binStart = start;
1570  binEnd = end;
1571  }
1572 
1573  int getBINStart() {
1574  return binStart;
1575  }
1576 
1577  int getBINEnd() {
1578  return binEnd;
1579  }
1580 
1581  long getCount() {
1582  return count;
1583  }
1584 
1585  boolean hasDetails() {
1586  return binRange != null;
1587  }
1588 
1589  @Override
1590  public Optional<Integer> getNumberLength() {
1591  return binRange.getNumberLength();
1592  }
1593 
1594  @Override
1595  public Optional<String> getBankCity() {
1596  return binRange.getBankCity();
1597  }
1598 
1599  @Override
1600  public Optional<String> getBankName() {
1601  return binRange.getBankName();
1602  }
1603 
1604  @Override
1605  public Optional<String> getBankPhoneNumber() {
1606  return binRange.getBankPhoneNumber();
1607  }
1608 
1609  @Override
1610  public Optional<String> getBankURL() {
1611  return binRange.getBankURL();
1612  }
1613 
1614  @Override
1615  public Optional<String> getBrand() {
1616  return binRange.getBrand();
1617  }
1618 
1619  @Override
1620  public Optional<String> getCardType() {
1621  return binRange.getCardType();
1622  }
1623 
1624  @Override
1625  public Optional<String> getCountry() {
1626  return binRange.getCountry();
1627  }
1628 
1629  @Override
1630  public Optional<String> getScheme() {
1631  return binRange.getScheme();
1632  }
1633  }
1634 
1635  final private class AccountArtifactNode extends BlackboardArtifactNode {
1636 
1637  private final BlackboardArtifact artifact;
1638 
1639  private AccountArtifactNode(BlackboardArtifact artifact) {
1640  super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1641  this.artifact = artifact;
1642  setName(Long.toString(this.artifact.getArtifactID()));
1643 
1644  reviewStatusBus.register(this);
1645  }
1646 
1647  @Override
1648  public Action[] getActions(boolean context) {
1649  List<Action> actionsList = new ArrayList<>();
1650  actionsList.addAll(Arrays.asList(super.getActions(context)));
1651 
1652  actionsList.add(approveActionInstance);
1653  actionsList.add(rejectActionInstance);
1654 
1655  return actionsList.toArray(new Action[actionsList.size()]);
1656  }
1657 
1658  @Override
1659  protected Sheet createSheet() {
1660  Sheet sheet = super.createSheet();
1661  Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1662  if (properties == null) {
1663  properties = Sheet.createPropertiesSet();
1664  sheet.put(properties);
1665  }
1666  properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1667  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1668  Bundle.Accounts_FileWithCCNNode_noDescription(),
1669  artifact.getReviewStatus().getDisplayName()));
1670 
1671  return sheet;
1672  }
1673 
1674  @Subscribe
1675  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1676 
1677  // Update the node if event includes this artifact
1678  event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1679  return _item;
1680  }).forEachOrdered((_item) -> {
1681  updateSheet();
1682  });
1683  }
1684 
1685  private void updateSheet() {
1686  this.setSheet(createSheet());
1687  }
1688 
1689  }
1690 
1691  @Deprecated
1692  private final class ToggleShowRejected extends AbstractAction {
1693 
1694  @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1695  ToggleShowRejected() {
1696  super(Bundle.ToggleShowRejected_name());
1697  }
1698 
1699  @Override
1700  public void actionPerformed(ActionEvent e) {
1701  showRejected = !showRejected;
1702  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1703  }
1704  }
1705 
1711  public void setShowRejected(boolean showRejected) {
1712  this.showRejected = showRejected;
1713  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1714  }
1715 
1716  private abstract class ReviewStatusAction extends AbstractAction {
1717 
1718  private final BlackboardArtifact.ReviewStatus newStatus;
1719 
1720  private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1721  super(displayName);
1722  this.newStatus = newStatus;
1723 
1724  }
1725 
1726  @Override
1727  public void actionPerformed(ActionEvent e) {
1728 
1729  /* get paths for selected nodes to reselect after applying review
1730  * status change */
1731  List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1732  .map(node -> {
1733  String[] createPath;
1734  /*
1735  * If the we are rejecting and not showing rejected
1736  * results, then the selected node, won't exist any
1737  * more, so we select the previous one in stead.
1738  */
1739  if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1740  List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1741  if (siblings.size() > 1) {
1742  int indexOf = siblings.indexOf(node);
1743  //there is no previous for the first node, so instead we select the next one
1744  Node sibling = indexOf > 0
1745  ? siblings.get(indexOf - 1)
1746  : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1747  createPath = NodeOp.createPath(sibling, null);
1748  } else {
1749  /* if there are no other siblings to select,
1750  * just return null, but note we need to filter
1751  * this out of stream below */
1752  return null;
1753  }
1754  } else {
1755  createPath = NodeOp.createPath(node, null);
1756  }
1757  //for the reselect to work we need to strip off the first part of the path.
1758  return Arrays.copyOfRange(createPath, 1, createPath.length);
1759  })
1760  .filter(Objects::nonNull)
1761  .collect(Collectors.toList());
1762 
1763  //change status of selected artifacts
1764  final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1765  artifacts.forEach(artifact -> {
1766  try {
1767  artifact.setReviewStatus(newStatus);
1768  } catch (TskCoreException ex) {
1769  LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1770  }
1771  });
1772  //post event
1773  reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1774 
1775  final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1776  final Node rootNode = directoryListing.getRootNode();
1777 
1778  //convert paths back to nodes
1779  List<Node> toArray = new ArrayList<>();
1780  selectedPaths.forEach(path -> {
1781  try {
1782  toArray.add(NodeOp.findPath(rootNode, path));
1783  } catch (NodeNotFoundException ex) { //NOPMD empty catch clause
1784  //just ingnore paths taht don't exist. this is expected since we are rejecting
1785  }
1786  });
1787  //select nodes
1788  directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1789  }
1790  }
1791 
1792  final private class ApproveAccounts extends ReviewStatusAction {
1793 
1794  @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1795  private ApproveAccounts() {
1796  super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1797  }
1798  }
1799 
1800  final private class RejectAccounts extends ReviewStatusAction {
1801 
1802  @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1803  private RejectAccounts() {
1804  super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1805  }
1806  }
1807 
1808  static private class ReviewStatusChangeEvent {
1809 
1810  Collection<? extends BlackboardArtifact> artifacts;
1811  BlackboardArtifact.ReviewStatus newReviewStatus;
1812 
1813  ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1814  this.artifacts = artifacts;
1815  this.newReviewStatus = newReviewStatus;
1816  }
1817  }
1818 
1824  public static String getIconFilePath(Account.Type type) {
1825 
1826  if (type.equals(Account.Type.CREDIT_CARD)) {
1827  return ICON_BASE_PATH + "credit-card.png";
1828  } else if (type.equals(Account.Type.DEVICE)) {
1829  return ICON_BASE_PATH + "image.png";
1830  } else if (type.equals(Account.Type.EMAIL)) {
1831  return ICON_BASE_PATH + "email.png";
1832  } else if (type.equals(Account.Type.FACEBOOK)) {
1833  return ICON_BASE_PATH + "facebook.png";
1834  } else if (type.equals(Account.Type.INSTAGRAM)) {
1835  return ICON_BASE_PATH + "instagram.png";
1836  } else if (type.equals(Account.Type.MESSAGING_APP)) {
1837  return ICON_BASE_PATH + "messaging.png";
1838  } else if (type.equals(Account.Type.PHONE)) {
1839  return ICON_BASE_PATH + "phone.png";
1840  } else if (type.equals(Account.Type.TWITTER)) {
1841  return ICON_BASE_PATH + "twitter.png";
1842  } else if (type.equals(Account.Type.WEBSITE)) {
1843  return ICON_BASE_PATH + "web-file.png";
1844  } else if (type.equals(Account.Type.WHATSAPP)) {
1845  return ICON_BASE_PATH + "WhatsApp.png";
1846  } else {
1847  //there could be a default icon instead...
1848  return ICON_BASE_PATH + "face.png";
1849 // throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
1850  }
1851  }
1852 }
boolean createKeys(List< CreditCardViewMode > list)
Definition: Accounts.java:624
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
Definition: Accounts.java:1191
static List< Action > getActions(File file, boolean isArtifactSource)
static synchronized BankIdentificationNumber getBINInfo(int bin)
static String getIconFilePath(Account.Type type)
Definition: Accounts.java:1824
void removeIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.ReviewStatus > statuses
Definition: Accounts.java:1139
void addIngestJobEventListener(final PropertyChangeListener listener)
BinResult(long count,@Nonnull BINRange binRange)
Definition: Accounts.java:1559
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Definition: Accounts.java:1240
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
Definition: Accounts.java:1141
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
Accounts(SleuthkitCase skCase, long objId)
Definition: Accounts.java:122
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:441
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
Definition: Accounts.java:1720
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:486

Copyright © 2012-2018 Basis Technology. Generated on: Fri Jun 21 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.