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

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