Autopsy  4.5.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-2017 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.logging.Logger;
44 import java.util.stream.Collectors;
45 import java.util.stream.Stream;
46 import javax.annotation.Nonnull;
47 import javax.annotation.concurrent.Immutable;
48 import javax.swing.AbstractAction;
49 import javax.swing.Action;
50 import org.apache.commons.lang3.StringUtils;
51 import org.openide.nodes.ChildFactory;
52 import org.openide.nodes.Children;
53 import org.openide.nodes.Node;
54 import org.openide.nodes.NodeNotFoundException;
55 import org.openide.nodes.NodeOp;
56 import org.openide.nodes.Sheet;
57 import org.openide.util.Exceptions;
58 import org.openide.util.NbBundle;
59 import org.openide.util.Utilities;
60 import org.openide.util.lookup.Lookups;
74 import org.sleuthkit.datamodel.AbstractFile;
75 import org.sleuthkit.datamodel.Account;
76 import org.sleuthkit.datamodel.AccountFileInstance;
77 import org.sleuthkit.datamodel.BlackboardArtifact;
78 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
79 import org.sleuthkit.datamodel.BlackboardAttribute;
80 import org.sleuthkit.datamodel.Content;
81 import org.sleuthkit.datamodel.SleuthkitCase;
82 import org.sleuthkit.datamodel.TskCoreException;
83 import org.sleuthkit.datamodel.TskData.DbType;
84 
89 final public class Accounts implements AutopsyVisitableItem {
90 
91  private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
92 
93  @NbBundle.Messages("AccountsRootNode.name=Accounts")
94  final public static String NAME = Bundle.AccountsRootNode_name();
95 
96  private SleuthkitCase skCase;
97  private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
98 
102  private boolean showRejected = false;
103 
106 
112  public Accounts(SleuthkitCase skCase) {
113  this.skCase = skCase;
114 
115  this.rejectActionInstance = new RejectAccounts();
116  this.approveActionInstance = new ApproveAccounts();
117  }
118 
119  @Override
120  public <T> T accept(AutopsyItemVisitor<T> v) {
121  return v.visit(this);
122  }
123 
132  return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
133  }
134 
142  public Action newToggleShowRejectedAction() {
143  return new ToggleShowRejected();
144  }
145 
152  private abstract class ObservingChildren<X> extends ChildFactory.Detachable<X> {
153 
159  super();
160  }
161 
166  abstract protected boolean createKeys(List<X> list);
167 
173  @Subscribe
174  abstract void handleReviewStatusChange(ReviewStatusChangeEvent event);
175 
176  @Subscribe
177  abstract void handleDataAdded(ModuleDataEvent event);
178 
179  @Override
180  protected void removeNotify() {
181  super.removeNotify();
182  reviewStatusBus.unregister(ObservingChildren.this);
183  }
184 
185  @Override
186  protected void addNotify() {
187  super.addNotify();
188  refresh(true);
189  reviewStatusBus.register(ObservingChildren.this);
190  }
191  }
192 
196  @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
197  final public class AccountsRootNode extends DisplayableItemNode {
198 
199  public AccountsRootNode() {
200  super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
201  setName(Accounts.NAME);
202  setDisplayName(Bundle.Accounts_RootNode_displayName());
203  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
204  }
205 
206  @Override
207  public boolean isLeafTypeNode() {
208  return false;
209  }
210 
211  @Override
212  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
213  return v.visit(this);
214  }
215 
216  @Override
217  public String getItemType() {
218  return getClass().getName();
219  }
220  }
221 
225  private class AccountTypeFactory extends ObservingChildren<String> {
226 
227  /*
228  * The pcl is in this class because it has the easiest mechanisms to
229  * add and remove itself during its life cycles.
230  */
231  private final PropertyChangeListener pcl = new PropertyChangeListener() {
232  @Override
233  public void propertyChange(PropertyChangeEvent evt) {
234  String eventType = evt.getPropertyName();
235  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
242  try {
250  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
251  if (null != eventData
252  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
253  reviewStatusBus.post(eventData);
254  }
255  } catch (IllegalStateException notUsed) {
256  // Case is closed, do nothing.
257  }
258  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
259  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
266  try {
268  refresh(true);
269  } catch (IllegalStateException notUsed) {
270  // Case is closed, do nothing.
271  }
272  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
273  // case was closed. Remove listeners so that we don't get called with a stale case handle
274  if (evt.getNewValue() == null) {
275  removeNotify();
276  skCase = null;
277  }
278  }
279  }
280  };
281 
282  @Subscribe
283  @Override
284  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
285  refresh(true);
286  }
287 
288  @Subscribe
289  @Override
290  void handleDataAdded(ModuleDataEvent event) {
291  refresh(true);
292  }
293 
294  @Override
295  protected boolean createKeys(List<String> list) {
296  try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(
297  "SELECT DISTINCT blackboard_attributes.value_text as account_type "
298  + " FROM blackboard_attributes "
299  + " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID());
300  ResultSet resultSet = executeQuery.getResultSet()) {
301  while (resultSet.next()) {
302  String accountType = resultSet.getString("account_type");
303  list.add(accountType);
304  }
305  } catch (TskCoreException | SQLException ex) {
306  LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
307  }
308 
309  return true;
310  }
311 
312  @Override
313  protected Node[] createNodesForKey(String key) {
314 
315  String accountType = key;
316  if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
317  return new Node[]{new CreditCardNumberAccountTypeNode()};
318  } else {
319  String accountTypeDisplayname;
320  try {
321  accountTypeDisplayname = skCase.getCommunicationsManager().getAccountType(accountType).getDisplayName();
322  } catch (TskCoreException ex) {
323  LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex);
324  accountTypeDisplayname = accountType;
325  }
326 
327  return new Node[]{new DefaultAccountTypeNode(key, accountTypeDisplayname)};
328  }
329 
330  }
331 
332  @Override
333  protected void removeNotify() {
337  super.removeNotify();
338  }
339 
340  @Override
341  protected void addNotify() {
345  super.addNotify();
346  refresh(true);
347  }
348 
349  }
350 
351  final private class DefaultAccountFactory extends ObservingChildren<Long> {
352 
353  private final String accountTypeName;
354 
355  private DefaultAccountFactory(String accountTypeName) {
356  this.accountTypeName = accountTypeName;
357  }
358 
359  private final PropertyChangeListener pcl = new PropertyChangeListener() {
360  @Override
361  public void propertyChange(PropertyChangeEvent evt) {
362  String eventType = evt.getPropertyName();
363  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
370  try {
378  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
379  if (null != eventData
380  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
381  reviewStatusBus.post(eventData);
382  }
383  } catch (IllegalStateException notUsed) {
384  // Case is closed, do nothing.
385  }
386  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
387  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
394  try {
396  refresh(true);
397 
398  } catch (IllegalStateException notUsed) {
399  // Case is closed, do nothing.
400  }
401  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
402  // case was closed. Remove listeners so that we don't get called with a stale case handle
403  if (evt.getNewValue() == null) {
404  removeNotify();
405  skCase = null;
406  }
407  }
408  }
409  };
410 
411  @Override
412  protected void addNotify() {
415  super.addNotify();
416  }
417 
418  @Override
419  protected void removeNotify() {
422  super.removeNotify();
423  }
424 
425  @Override
426  protected boolean createKeys(List<Long> list) {
427  String query
428  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
429  + " FROM blackboard_artifacts " //NON-NLS
430  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
431  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
432  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
433  + " AND blackboard_attributes.value_text = '" + accountTypeName + "'" //NON-NLS
434  + getRejectedArtifactFilterClause(); //NON-NLS
435  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
436  ResultSet rs = results.getResultSet();) {
437  while (rs.next()) {
438  list.add(rs.getLong("artifact_id")); //NON-NLS
439  }
440  } catch (TskCoreException | SQLException ex) {
441  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
442  }
443 
444  return true;
445  }
446 
447  @Override
448  protected Node[] createNodesForKey(Long t) {
449  try {
450  return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))};
451  } catch (TskCoreException ex) {
452  LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
453  return new Node[0];
454  }
455  }
456 
457  @Subscribe
458  @Override
459  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
460  refresh(true);
461  }
462 
463  @Subscribe
464  @Override
465  void handleDataAdded(ModuleDataEvent event) {
466  refresh(true);
467  }
468  }
469 
474  final public class DefaultAccountTypeNode extends DisplayableItemNode {
475 
476  private DefaultAccountTypeNode(String accountTypeName, String accountTypeDisplayName) {
477  super(Children.create(new DefaultAccountFactory(accountTypeName), true), Lookups.singleton(accountTypeDisplayName));
478  setName(accountTypeDisplayName);
479  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
480  }
481 
482  @Override
483  public boolean isLeafTypeNode() {
484  return true;
485  }
486 
487  @Override
488  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
489  return v.visit(this);
490  }
491 
492  @Override
493  public String getItemType() {
494  return getClass().getName();
495  }
496  }
497 
501  private enum CreditCardViewMode {
504  }
505 
506  final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
507 
508  private final PropertyChangeListener pcl = new PropertyChangeListener() {
509  @Override
510  public void propertyChange(PropertyChangeEvent evt) {
511  String eventType = evt.getPropertyName();
512  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
519  try {
527  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
528  if (null != eventData
529  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
530  reviewStatusBus.post(eventData);
531  }
532  } catch (IllegalStateException notUsed) {
533  // Case is closed, do nothing.
534  }
535  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
536  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
543  try {
545  refresh(true);
546 
547  } catch (IllegalStateException notUsed) {
548  // Case is closed, do nothing.
549  }
550  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
551  // case was closed. Remove listeners so that we don't get called with a stale case handle
552  if (evt.getNewValue() == null) {
553  removeNotify();
554  skCase = null;
555  }
556  }
557  }
558  };
559 
560  @Subscribe
561  @Override
562  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
563  refresh(true);
564  }
565 
566  @Subscribe
567  @Override
568  void handleDataAdded(ModuleDataEvent event) {
569  refresh(true);
570  }
571 
572  @Override
573  protected void addNotify() {
576  super.addNotify();
577  }
578 
579  @Override
580  protected void removeNotify() {
583  super.removeNotify();
584  }
585 
589  @Override
590  protected boolean createKeys(List<CreditCardViewMode> list) {
591  list.addAll(Arrays.asList(CreditCardViewMode.values()));
592 
593  return true;
594  }
595 
596  @Override
597  protected Node[] createNodesForKey(CreditCardViewMode key) {
598  switch (key) {
599  case BY_BIN:
600  return new Node[]{new ByBINNode()};
601  case BY_FILE:
602  return new Node[]{new ByFileNode()};
603  default:
604  return new Node[0];
605  }
606  }
607  }
608 
613 
619  super(Children.create(new ViewModeFactory(), true), Lookups.singleton(Account.Type.CREDIT_CARD.getDisplayName()));
620  setName(Account.Type.CREDIT_CARD.getDisplayName());
621  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
622  }
623 
624  @Override
625  public boolean isLeafTypeNode() {
626  return false;
627  }
628 
629  @Override
630  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
631  return v.visit(this);
632  }
633 
634  @Override
635  public String getItemType() {
636  return getClass().getName();
637  }
638  }
639 
640  final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
641 
642  private final PropertyChangeListener pcl = new PropertyChangeListener() {
643  @Override
644  public void propertyChange(PropertyChangeEvent evt) {
645  String eventType = evt.getPropertyName();
646  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
653  try {
661  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
662  if (null != eventData
663  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
664  reviewStatusBus.post(eventData);
665  }
666  } catch (IllegalStateException notUsed) {
667  // Case is closed, do nothing.
668  }
669  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
670  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
677  try {
679  refresh(true);
680 
681  } catch (IllegalStateException notUsed) {
682  // Case is closed, do nothing.
683  }
684  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
685  // case was closed. Remove listeners so that we don't get called with a stale case handle
686  if (evt.getNewValue() == null) {
687  removeNotify();
688  skCase = null;
689  }
690  }
691  }
692  };
693 
694  @Override
695  protected void addNotify() {
698  super.addNotify();
699  }
700 
701  @Override
702  protected void removeNotify() {
705  super.removeNotify();
706  }
707 
708  @Subscribe
709  @Override
710  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
711  refresh(true);
712  }
713 
714  @Subscribe
715  @Override
716  void handleDataAdded(ModuleDataEvent event) {
717  refresh(true);
718  }
719 
720  @Override
721  protected boolean createKeys(List<FileWithCCN> list) {
722  String query
723  = "SELECT blackboard_artifacts.obj_id," //NON-NLS
724  + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
725  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
726  query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
727  + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
728  } else {
729  query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
730  + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
731  }
732  query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
733  + " FROM blackboard_artifacts " //NON-NLS
734  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
735  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
736  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
737  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
738  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
739  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
741  + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
742  + " ORDER BY hits DESC "; //NON-NLS
743  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
744  ResultSet rs = results.getResultSet();) {
745  while (rs.next()) {
746  list.add(new FileWithCCN(
747  rs.getLong("obj_id"), //NON-NLS
748  rs.getString("solr_document_id"), //NON-NLS
749  unGroupConcat(rs.getString("artifact_IDs"), Long::valueOf), //NON-NLS
750  rs.getLong("hits"), //NON-NLS
751  new HashSet<>(unGroupConcat(rs.getString("review_status_ids"), id -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(id)))))); //NON-NLS
752  }
753  } catch (TskCoreException | SQLException ex) {
754  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
755 
756  }
757  return true;
758  }
759 
760  @Override
761  protected Node[] createNodesForKey(FileWithCCN key) {
762  //add all account artifacts for the file and the file itself to the lookup
763  try {
764  List<Object> lookupContents = new ArrayList<>();
765  for (long artId : key.artifactIDs) {
766  lookupContents.add(skCase.getBlackboardArtifact(artId));
767  }
768  AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID());
769  lookupContents.add(abstractFileById);
770  return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
771  } catch (TskCoreException ex) {
772  LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
773  return new Node[0];
774  }
775  }
776  }
777 
782  final public class ByFileNode extends DisplayableItemNode {
783 
787  private ByFileNode() {
788  super(Children.create(new FileWithCCNFactory(), true), Lookups.singleton("By File"));
789  setName("By File"); //NON-NLS
790  updateDisplayName();
791  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
792  reviewStatusBus.register(this);
793  }
794 
795  @NbBundle.Messages({
796  "# {0} - number of children",
797  "Accounts.ByFileNode.displayName=By File ({0})"})
798  private void updateDisplayName() {
799  String query
800  = "SELECT count(*) FROM ( SELECT count(*) AS documents "
801  + " FROM blackboard_artifacts " //NON-NLS
802  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
803  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
804  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
805  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
806  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
807  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
809  + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
810  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
811  ResultSet rs = results.getResultSet();) {
812  while (rs.next()) {
813  if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
814  setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count")));
815  } else {
816  setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count(*)")));
817  }
818  }
819  } catch (TskCoreException | SQLException ex) {
820  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
821 
822  }
823  }
824 
825  @Override
826  public boolean isLeafTypeNode() {
827  return true;
828  }
829 
830  @Override
831  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
832  return v.visit(this);
833  }
834 
835  @Override
836  public String getItemType() {
837  return getClass().getName();
838  }
839 
840  @Subscribe
841  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
842  updateDisplayName();
843  }
844 
845  @Subscribe
846  void handleDataAdded(ModuleDataEvent event) {
847  updateDisplayName();
848  }
849  }
850 
851  final private class BINFactory extends ObservingChildren<BinResult> {
852 
853  private final PropertyChangeListener pcl = new PropertyChangeListener() {
854  @Override
855  public void propertyChange(PropertyChangeEvent evt) {
856  String eventType = evt.getPropertyName();
857  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
864  try {
872  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
873  if (null != eventData
874  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
875  reviewStatusBus.post(eventData);
876  }
877  } catch (IllegalStateException notUsed) {
878  // Case is closed, do nothing.
879  }
880  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
881  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
888  try {
890 
891  refresh(true);
892  } catch (IllegalStateException notUsed) {
893  // Case is closed, do nothing.
894  }
895  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
896  // case was closed. Remove listeners so that we don't get called with a stale case handle
897  if (evt.getNewValue() == null) {
898  removeNotify();
899  skCase = null;
900  }
901  }
902  }
903  };
904 
905  @Override
906  protected void addNotify() {
909  super.addNotify();
910  }
911 
912  @Override
913  protected void removeNotify() {
916  super.removeNotify();
917  }
918 
919  @Subscribe
920  @Override
921  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
922  refresh(true);
923  }
924 
925  @Subscribe
926  @Override
927  void handleDataAdded(ModuleDataEvent event) {
928  refresh(true);
929  }
930 
931  @Override
932  protected boolean createKeys(List<BinResult> list) {
933 
934  RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
935 
936  String query
937  = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
938  + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
939  + " FROM blackboard_artifacts " //NON-NLS
940  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
941  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
942  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
944  + " GROUP BY BIN " //NON-NLS
945  + " ORDER BY BIN "; //NON-NLS
946  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
947  ResultSet resultSet = results.getResultSet();
948  //sort all te individual bins in to the ranges
949  while (resultSet.next()) {
950  final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
951  long count = resultSet.getLong("count");
952 
953  BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
954  BinResult previousResult = binRanges.get(bin);
955 
956  if (previousResult != null) {
957  binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
958  count += previousResult.getCount();
959  }
960 
961  if (binRange != null) {
962  binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
963  } else {
964  binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
965  }
966  }
967  binRanges.asMapOfRanges().values().forEach(list::add);
968  } catch (TskCoreException | SQLException ex) {
969  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
970 
971  }
972 
973  return true;
974  }
975 
976  @Override
977  protected Node[] createNodesForKey(BinResult key) {
978  return new Node[]{new BINNode(key)};
979  }
980  }
981 
986  final public class ByBINNode extends DisplayableItemNode {
987 
991  @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
992  private ByBINNode() {
993  super(Children.create(new BINFactory(), true), Lookups.singleton(Bundle.Accounts_ByBINNode_name()));
994  setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
995  updateDisplayName();
996  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
997  reviewStatusBus.register(this);
998  }
999 
1000  @NbBundle.Messages({
1001  "# {0} - number of children",
1002  "Accounts.ByBINNode.displayName=By BIN ({0})"})
1003  private void updateDisplayName() {
1004  String query
1005  = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
1006  + " FROM blackboard_artifacts " //NON-NLS
1007  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
1008  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1009  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1010  + getRejectedArtifactFilterClause(); //NON-NLS
1011  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
1012  ResultSet resultSet = results.getResultSet();
1013  while (resultSet.next()) {
1014  setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
1015  }
1016  } catch (TskCoreException | SQLException ex) {
1017  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
1018  }
1019  }
1020 
1021  @Override
1022  public boolean isLeafTypeNode() {
1023  return false;
1024  }
1025 
1026  @Override
1027  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
1028  return v.visit(this);
1029  }
1030 
1031  @Override
1032  public String getItemType() {
1033  return getClass().getName();
1034  }
1035 
1036  @Subscribe
1037  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1038  updateDisplayName();
1039  }
1040 
1041  @Subscribe
1042  void handleDataAdded(ModuleDataEvent event) {
1043  updateDisplayName();
1044  }
1045  }
1046 
1051  @Immutable
1052  final private static class FileWithCCN {
1053 
1054  @Override
1055  public int hashCode() {
1056  int hash = 5;
1057  hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
1058  hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
1059  hash = 79 * hash + Objects.hashCode(this.artifactIDs);
1060  hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
1061  hash = 79 * hash + Objects.hashCode(this.statuses);
1062  return hash;
1063  }
1064 
1065  @Override
1066  public boolean equals(Object obj) {
1067  if (this == obj) {
1068  return true;
1069  }
1070  if (obj == null) {
1071  return false;
1072  }
1073  if (getClass() != obj.getClass()) {
1074  return false;
1075  }
1076  final FileWithCCN other = (FileWithCCN) obj;
1077  if (this.objID != other.objID) {
1078  return false;
1079  }
1080  if (this.hits != other.hits) {
1081  return false;
1082  }
1083  if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
1084  return false;
1085  }
1086  if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
1087  return false;
1088  }
1089  if (!Objects.equals(this.statuses, other.statuses)) {
1090  return false;
1091  }
1092  return true;
1093  }
1094 
1095  private final long objID;
1096  private final String keywordSearchDocID;
1097  private final List<Long> artifactIDs;
1098  private final long hits;
1099  private final Set<BlackboardArtifact.ReviewStatus> statuses;
1100 
1101  private FileWithCCN(long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
1102  this.objID = objID;
1103  this.keywordSearchDocID = solrDocID;
1104  this.artifactIDs = artifactIDs;
1105  this.hits = hits;
1106  this.statuses = statuses;
1107  }
1108 
1114  public long getObjID() {
1115  return objID;
1116  }
1117 
1124  public String getkeywordSearchDocID() {
1125  return keywordSearchDocID;
1126  }
1127 
1133  public List<Long> getArtifactIDs() {
1134  return artifactIDs;
1135  }
1136 
1142  public long getHits() {
1143  return hits;
1144  }
1145 
1151  public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
1152  return statuses;
1153  }
1154  }
1155 
1172  static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
1173  return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
1174  : Stream.of(groupConcat.split(",")) //NON-NLS
1175  .map(mapper::apply)
1176  .collect(Collectors.toList());
1177  }
1178 
1182  final public class FileWithCCNNode extends DisplayableItemNode {
1183 
1184  private final FileWithCCN fileKey;
1185  private final String fileName;
1186 
1195  @NbBundle.Messages({
1196  "# {0} - raw file name",
1197  "# {1} - solr chunk id",
1198  "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
1199  private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
1200  super(Children.LEAF, Lookups.fixed(lookupContents));
1201  this.fileKey = key;
1202  this.fileName = (key.getkeywordSearchDocID() == null)
1203  ? content.getName()
1204  : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
1205  setName(fileName + key.getObjID());
1206  setDisplayName(fileName);
1207  }
1208 
1209  @Override
1210  public boolean isLeafTypeNode() {
1211  return true;
1212  }
1213 
1214  @Override
1215  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
1216  return v.visit(this);
1217  }
1218 
1219  @Override
1220  public String getItemType() {
1221  return getClass().getName();
1222  }
1223 
1224  @Override
1225  @NbBundle.Messages({
1226  "Accounts.FileWithCCNNode.nameProperty.displayName=File",
1227  "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
1228  "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
1229  "Accounts.FileWithCCNNode.noDescription=no description"})
1230  protected Sheet createSheet() {
1231  Sheet s = super.createSheet();
1232  Sheet.Set ss = s.get(Sheet.PROPERTIES);
1233  if (ss == null) {
1234  ss = Sheet.createPropertiesSet();
1235  s.put(ss);
1236  }
1237 
1238  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1239  Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
1240  Bundle.Accounts_FileWithCCNNode_noDescription(),
1241  fileName));
1242  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1243  Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
1244  Bundle.Accounts_FileWithCCNNode_noDescription(),
1245  fileKey.getHits()));
1246  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1247  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1248  Bundle.Accounts_FileWithCCNNode_noDescription(),
1249  fileKey.getStatuses().stream()
1250  .map(BlackboardArtifact.ReviewStatus::getDisplayName)
1251  .collect(Collectors.joining(", ")))); //NON-NLS
1252 
1253  return s;
1254  }
1255 
1256  @Override
1257  public Action[] getActions(boolean context) {
1258  Action[] actions = super.getActions(context);
1259  ArrayList<Action> arrayList = new ArrayList<>();
1260  arrayList.addAll(Arrays.asList(actions));
1261  try {
1262  arrayList.addAll(DataModelActionsFactory.getActions(Accounts.this.skCase.getContentById(fileKey.getObjID()), false));
1263  } catch (TskCoreException ex) {
1264  LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1265  }
1266 
1267  arrayList.add(approveActionInstance);
1268  arrayList.add(rejectActionInstance);
1269 
1270  return arrayList.toArray(new Action[arrayList.size()]);
1271  }
1272  }
1273 
1274  final private class CreditCardNumberFactory extends ObservingChildren<Long> {
1275 
1276  private final BinResult bin;
1277 
1279  this.bin = bin;
1280  }
1281 
1282  @Subscribe
1283  @Override
1284  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1285  refresh(true);
1286  }
1287 
1288  @Subscribe
1289  @Override
1290  void handleDataAdded(ModuleDataEvent event) {
1291  refresh(true);
1292  }
1293 
1294  @Override
1295  protected boolean createKeys(List<Long> list) {
1296 
1297  String query
1298  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1299  + " FROM blackboard_artifacts " //NON-NLS
1300  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1301  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1302  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1303  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1305  + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1306  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1307  ResultSet rs = results.getResultSet();) {
1308  while (rs.next()) {
1309  list.add(rs.getLong("artifact_id")); //NON-NLS
1310  }
1311  } catch (TskCoreException | SQLException ex) {
1312  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1313 
1314  }
1315  return true;
1316  }
1317 
1318  @Override
1319  protected Node[] createNodesForKey(Long artifactID) {
1320  if (skCase == null) {
1321  return new Node[0];
1322  }
1323 
1324  try {
1325  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1326  return new Node[]{new AccountArtifactNode(art)};
1327  } catch (TskCoreException ex) {
1328  LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1329  return new Node[0];
1330  }
1331  }
1332  }
1333 
1334  private String getBinRangeString(BinResult bin) {
1335  if (bin.getBINStart() == bin.getBINEnd()) {
1336  return Integer.toString(bin.getBINStart());
1337  } else {
1338  return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1339  }
1340  }
1341 
1342  final public class BINNode extends DisplayableItemNode {
1343 
1347  private final BinResult bin;
1348 
1349  private BINNode(BinResult bin) {
1350  super(Children.create(new CreditCardNumberFactory(bin), true), Lookups.singleton(getBinRangeString(bin)));
1351  this.bin = bin;
1352  setName(getBinRangeString(bin));
1353  updateDisplayName();
1354  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1355  reviewStatusBus.register(this);
1356  }
1357 
1358  @Subscribe
1359  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1360  updateDisplayName();
1361  updateSheet();
1362  }
1363 
1364  @Subscribe
1365  void handleDataAdded(ModuleDataEvent event) {
1366  updateDisplayName();
1367  }
1368 
1369  private void updateDisplayName() {
1370  String query
1371  = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1372  + " FROM blackboard_artifacts " //NON-NLS
1373  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1374  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1375  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1376  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1378  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1379  ResultSet rs = results.getResultSet();) {
1380  while (rs.next()) {
1381  setDisplayName(getBinRangeString(bin) + " (" + rs.getLong("count") + ")"); //NON-NLS
1382  }
1383  } catch (TskCoreException | SQLException ex) {
1384  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1385 
1386  }
1387 
1388  }
1389 
1390  @Override
1391  public boolean isLeafTypeNode() {
1392  return true;
1393  }
1394 
1395  @Override
1396  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
1397  return v.visit(this);
1398  }
1399 
1400  @Override
1401  public String getItemType() {
1402  return getClass().getName();
1403  }
1404 
1405  private Sheet.Set getPropertySet(Sheet s) {
1406  Sheet.Set ss = s.get(Sheet.PROPERTIES);
1407  if (ss == null) {
1408  ss = Sheet.createPropertiesSet();
1409  s.put(ss);
1410  }
1411  return ss;
1412  }
1413 
1414  @Override
1415  @NbBundle.Messages({
1416  "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1417  "Accounts.BINNode.accountsProperty.displayName=Accounts",
1418  "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1419  "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1420  "Accounts.BINNode.brandProperty.displayName=Brand",
1421  "Accounts.BINNode.bankProperty.displayName=Bank",
1422  "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1423  "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1424  "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1425  "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1426  "Accounts.BINNode.noDescription=no description"})
1427  protected Sheet createSheet() {
1428  Sheet sheet = super.createSheet();
1429  Sheet.Set properties = getPropertySet(sheet);
1430 
1431  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1432  Bundle.Accounts_BINNode_binProperty_displayName(),
1433  Bundle.Accounts_BINNode_noDescription(),
1434  getBinRangeString(bin)));
1435  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1436  Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1437  bin.getCount()));
1438 
1439  //add optional properties if they are available
1440  if (bin.hasDetails()) {
1441  bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1442  Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1443  cardType)));
1444  bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1445  Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1446  scheme)));
1447  bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1448  Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1449  brand)));
1450  bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1451  Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1452  bankName)));
1453  bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1454  Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1455  bankCity)));
1456  bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1457  Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1458  country)));
1459  bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1460  Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1461  phoneNumber)));
1462  bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1463  Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1464  url)));
1465  }
1466  return sheet;
1467  }
1468 
1469  private void updateSheet() {
1470  this.setSheet(createSheet());
1471  }
1472 
1473  }
1474 
1479  @Immutable
1480  final static private class BinResult implements CreditCards.BankIdentificationNumber {
1481 
1482  @Override
1483  public int hashCode() {
1484  int hash = 3;
1485  hash = 97 * hash + this.binEnd;
1486  hash = 97 * hash + this.binStart;
1487  return hash;
1488  }
1489 
1490  @Override
1491  public boolean equals(Object obj) {
1492  if (this == obj) {
1493  return true;
1494  }
1495  if (obj == null) {
1496  return false;
1497  }
1498  if (getClass() != obj.getClass()) {
1499  return false;
1500  }
1501  final BinResult other = (BinResult) obj;
1502  if (this.binEnd != other.binEnd) {
1503  return false;
1504  }
1505  if (this.binStart != other.binStart) {
1506  return false;
1507  }
1508  return true;
1509  }
1510 
1514  private final long count;
1515 
1516  private final BINRange binRange;
1517  private final int binEnd;
1518  private final int binStart;
1519 
1520  private BinResult(long count, @Nonnull BINRange binRange) {
1521  this.count = count;
1522  this.binRange = binRange;
1523  binStart = binRange.getBINstart();
1524  binEnd = binRange.getBINend();
1525  }
1526 
1527  private BinResult(long count, int start, int end) {
1528  this.count = count;
1529  this.binRange = null;
1530  binStart = start;
1531  binEnd = end;
1532  }
1533 
1534  int getBINStart() {
1535  return binStart;
1536  }
1537 
1538  int getBINEnd() {
1539  return binEnd;
1540  }
1541 
1542  long getCount() {
1543  return count;
1544  }
1545 
1546  boolean hasDetails() {
1547  return binRange != null;
1548  }
1549 
1550  @Override
1551  public Optional<Integer> getNumberLength() {
1552  return binRange.getNumberLength();
1553  }
1554 
1555  @Override
1556  public Optional<String> getBankCity() {
1557  return binRange.getBankCity();
1558  }
1559 
1560  @Override
1561  public Optional<String> getBankName() {
1562  return binRange.getBankName();
1563  }
1564 
1565  @Override
1566  public Optional<String> getBankPhoneNumber() {
1567  return binRange.getBankPhoneNumber();
1568  }
1569 
1570  @Override
1571  public Optional<String> getBankURL() {
1572  return binRange.getBankURL();
1573  }
1574 
1575  @Override
1576  public Optional<String> getBrand() {
1577  return binRange.getBrand();
1578  }
1579 
1580  @Override
1581  public Optional<String> getCardType() {
1582  return binRange.getCardType();
1583  }
1584 
1585  @Override
1586  public Optional<String> getCountry() {
1587  return binRange.getCountry();
1588  }
1589 
1590  @Override
1591  public Optional<String> getScheme() {
1592  return binRange.getScheme();
1593  }
1594  }
1595 
1596  final private class AccountArtifactNode extends BlackboardArtifactNode {
1597 
1598  private final BlackboardArtifact artifact;
1599 
1600  private AccountArtifactNode(BlackboardArtifact artifact) {
1601  super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1602  this.artifact = artifact;
1603  setName("" + this.artifact.getArtifactID());
1604 
1605  reviewStatusBus.register(this);
1606  }
1607 
1608  @Override
1609  public Action[] getActions(boolean context) {
1610  List<Action> actionsList = new ArrayList<>();
1611  actionsList.addAll(Arrays.asList(super.getActions(context)));
1612 
1613  actionsList.add(approveActionInstance);
1614  actionsList.add(rejectActionInstance);
1615 
1616  return actionsList.toArray(new Action[actionsList.size()]);
1617  }
1618 
1619  @Override
1620  protected Sheet createSheet() {
1621  Sheet sheet = super.createSheet();
1622  Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1623  if (properties == null) {
1624  properties = Sheet.createPropertiesSet();
1625  sheet.put(properties);
1626  }
1627  properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1628  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1629  Bundle.Accounts_FileWithCCNNode_noDescription(),
1630  artifact.getReviewStatus().getDisplayName()));
1631 
1632  return sheet;
1633  }
1634 
1635  @Subscribe
1636  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1637 
1638  // Update the node if event includes this artifact
1639  event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> {
1640  return _item;
1641  }).forEachOrdered((_item) -> {
1642  updateSheet();
1643  });
1644  }
1645 
1646  private void updateSheet() {
1647  this.setSheet(createSheet());
1648  }
1649 
1650  }
1651 
1652  private final class ToggleShowRejected extends AbstractAction {
1653 
1654  @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1655  ToggleShowRejected() {
1656  super(Bundle.ToggleShowRejected_name());
1657  }
1658 
1659  @Override
1660  public void actionPerformed(ActionEvent e) {
1661  showRejected = !showRejected;
1662  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1663  }
1664  }
1665 
1666  private abstract class ReviewStatusAction extends AbstractAction {
1667 
1668  private final BlackboardArtifact.ReviewStatus newStatus;
1669 
1670  private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1671  super(displayName);
1672  this.newStatus = newStatus;
1673 
1674  }
1675 
1676  @Override
1677  public void actionPerformed(ActionEvent e) {
1678 
1679  /* get paths for selected nodes to reselect after applying review
1680  * status change */
1681  List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1682  .map(node -> {
1683  String[] createPath;
1684  /*
1685  * If the we are rejecting and not showing rejected
1686  * results, then the selected node, won't exist any
1687  * more, so we select the previous one in stead.
1688  */
1689  if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1690  List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1691  if (siblings.size() > 1) {
1692  int indexOf = siblings.indexOf(node);
1693  //there is no previous for the first node, so instead we select the next one
1694  Node sibling = indexOf > 0
1695  ? siblings.get(indexOf - 1)
1696  : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1697  createPath = NodeOp.createPath(sibling, null);
1698  } else {
1699  /* if there are no other siblings to select,
1700  * just return null, but note we need to filter
1701  * this out of stream below */
1702  return null;
1703  }
1704  } else {
1705  createPath = NodeOp.createPath(node, null);
1706  }
1707  //for the reselect to work we need to strip off the first part of the path.
1708  return Arrays.copyOfRange(createPath, 1, createPath.length);
1709  })
1710  .filter(Objects::nonNull)
1711  .collect(Collectors.toList());
1712 
1713  //change status of selected artifacts
1714  final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1715  artifacts.forEach(artifact -> {
1716  try {
1717  artifact.setReviewStatus(newStatus);
1718  } catch (TskCoreException ex) {
1719  LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1720  }
1721  });
1722  //post event
1723  reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1724 
1725  final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1726  final Node rootNode = directoryListing.getRootNode();
1727 
1728  //convert paths back to nodes
1729  List<Node> toArray = new ArrayList<>();
1730  selectedPaths.forEach(path -> {
1731  try {
1732  toArray.add(NodeOp.findPath(rootNode, path));
1733  } catch (NodeNotFoundException ex) {
1734  //just ingnore paths taht don't exist. this is expected since we are rejecting
1735  }
1736  });
1737  //select nodes
1738  directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1739  }
1740  }
1741 
1742  final private class ApproveAccounts extends ReviewStatusAction {
1743 
1744  @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1745  private ApproveAccounts() {
1746  super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1747  }
1748  }
1749 
1750  final private class RejectAccounts extends ReviewStatusAction {
1751 
1752  @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1753  private RejectAccounts() {
1754  super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1755  }
1756  }
1757 
1758  private class ReviewStatusChangeEvent {
1759 
1760  Collection<? extends BlackboardArtifact> artifacts;
1761  BlackboardArtifact.ReviewStatus newReviewStatus;
1762 
1763  public ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1764  this.artifacts = artifacts;
1765  this.newReviewStatus = newReviewStatus;
1766  }
1767  }
1768 }
boolean createKeys(List< CreditCardViewMode > list)
Definition: Accounts.java:590
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
Definition: Accounts.java:1151
static List< Action > getActions(File file, boolean isArtifactSource)
static synchronized BankIdentificationNumber getBINInfo(int bin)
void removeIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.ReviewStatus > statuses
Definition: Accounts.java:1099
void addIngestJobEventListener(final PropertyChangeListener listener)
ReviewStatusChangeEvent(Collection<?extends BlackboardArtifact > artifacts, BlackboardArtifact.ReviewStatus newReviewStatus)
Definition: Accounts.java:1763
BinResult(long count,@Nonnull BINRange binRange)
Definition: Accounts.java:1520
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Definition: Accounts.java:1199
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
Definition: Accounts.java:1101
void addIngestModuleEventListener(final PropertyChangeListener listener)
DefaultAccountTypeNode(String accountTypeName, String accountTypeDisplayName)
Definition: Accounts.java:476
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:419
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
Definition: Accounts.java:1670
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:464

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