Autopsy  4.4.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-2016 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.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;
72 import org.sleuthkit.datamodel.AbstractFile;
73 import org.sleuthkit.datamodel.Account;
74 import org.sleuthkit.datamodel.BlackboardArtifact;
75 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
76 import org.sleuthkit.datamodel.BlackboardAttribute;
77 import org.sleuthkit.datamodel.Content;
78 import org.sleuthkit.datamodel.SleuthkitCase;
79 import org.sleuthkit.datamodel.TskCoreException;
80 import org.sleuthkit.datamodel.TskData.DbType;
81 
86 final public class Accounts implements AutopsyVisitableItem {
87 
88  private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
89 
90  @NbBundle.Messages("AccountsRootNode.name=Accounts")
91  final public static String NAME = Bundle.AccountsRootNode_name();
92 
93  private SleuthkitCase skCase;
94  private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
95 
99  private boolean showRejected = false;
100 
103 
109  public Accounts(SleuthkitCase skCase) {
110  this.skCase = skCase;
111 
112  this.rejectActionInstance = new RejectAccounts();
113  this.approveActionInstance = new ApproveAccounts();
114  }
115 
116  @Override
117  public <T> T accept(AutopsyItemVisitor<T> v) {
118  return v.visit(this);
119  }
120 
129  return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
130  }
131 
139  public Action newToggleShowRejectedAction() {
140  return new ToggleShowRejected();
141  }
142 
149  private abstract class ObservingChildren<X> extends Children.Keys<X> {
150 
156  super(true);
157  }
158 
163  abstract protected Collection<X> createKeys();
164 
168  void refreshKeys() {
169  setKeys(createKeys());
170  }
171 
177  @Subscribe
178  abstract void handleReviewStatusChange(ReviewStatusChangeEvent event);
179 
180  @Subscribe
181  abstract void handleDataAdded(ModuleDataEvent event);
182 
183  @Override
184  protected void removeNotify() {
185  super.removeNotify();
186  reviewStatusBus.unregister(ObservingChildren.this);
187  }
188 
189  @Override
190  protected void addNotify() {
191  super.addNotify();
192  refreshKeys();
193  reviewStatusBus.register(ObservingChildren.this);
194  }
195  }
196 
200  @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
201  final public class AccountsRootNode extends DisplayableItemNode {
202 
206  final private class AccountTypeFactory extends ObservingChildren<String> {
207 
208  /*
209  * The pcl is in this class because it has the easiest mechanisms to
210  * add and remove itself during its life cycles.
211  */
212  private final PropertyChangeListener pcl = new PropertyChangeListener() {
213  @Override
214  public void propertyChange(PropertyChangeEvent evt) {
215  String eventType = evt.getPropertyName();
216  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
223  try {
232  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
233  if (null != eventData
234  && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
235  reviewStatusBus.post(eventData);
236  }
237  } catch (IllegalStateException notUsed) {
238  // Case is closed, do nothing.
239  }
240  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
241  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
248  try {
250  refreshKeys();
251  } catch (IllegalStateException notUsed) {
252  // Case is closed, do nothing.
253  }
254  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
255  // case was closed. Remove listeners so that we don't get called with a stale case handle
256  if (evt.getNewValue() == null) {
257  removeNotify();
258  skCase = null;
259  }
260  }
261  }
262  };
263 
264  @Subscribe
265  @Override
266  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
267  refreshKeys();
268  }
269 
270  @Subscribe
271  @Override
272  void handleDataAdded(ModuleDataEvent event) {
273  refreshKeys();
274  }
275 
276  @Override
277  protected List<String> createKeys() {
278  List<String> list = new ArrayList<>();
279  try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(
280  "SELECT DISTINCT blackboard_attributes.value_text as account_type "
281  + " FROM blackboard_attributes "
282  + " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID());
283  ResultSet resultSet = executeQuery.getResultSet()) {
284  while (resultSet.next()) {
285  String accountType = resultSet.getString("account_type");
286  list.add(accountType);
287  }
288  } catch (TskCoreException | SQLException ex) {
289  LOGGER.log(Level.SEVERE, "Error querying for account_types", ex);
290  }
291  return list;
292  }
293 
294  @Override
295  protected Node[] createNodes(String key) {
296  try {
297  Account.Type accountType = Account.Type.valueOf(key);
298  switch (accountType) {
299  case CREDIT_CARD:
300  return new Node[]{new CreditCardNumberAccountTypeNode()};
301  default:
302  return new Node[]{new DefaultAccountTypeNode(key)};
303  }
304  } catch (IllegalArgumentException ex) {
305  LOGGER.log(Level.WARNING, "Unknown account type: {0}", key);
306  //Flesh out what happens with other account types here.
307  return new Node[]{new DefaultAccountTypeNode(key)};
308  }
309  }
310 
311  @Override
312  protected void removeNotify() {
316  super.removeNotify();
317  }
318 
319  @Override
320  protected void addNotify() {
324  super.addNotify();
325  refreshKeys();
326  }
327 
328  }
329 
330  public AccountsRootNode() {
331  super(Children.LEAF, Lookups.singleton(Accounts.this));
332  setChildren(Children.createLazy(AccountTypeFactory::new));
333  setName(Accounts.NAME);
334  setDisplayName(Bundle.Accounts_RootNode_displayName());
335  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
336  }
337 
338  @Override
339  public boolean isLeafTypeNode() {
340  return false;
341  }
342 
343  @Override
344  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
345  return v.visit(this);
346  }
347 
348  @Override
349  public String getItemType() {
350  return getClass().getName();
351  }
352  }
353 
358  final public class DefaultAccountTypeNode extends DisplayableItemNode {
359 
360  private final String accountTypeName;
361 
362  final private class DefaultAccountFactory extends ObservingChildren<Long> {
363 
365  }
366 
367  @Override
368  protected Collection<Long> createKeys() {
369  List<Long> list = new ArrayList<>();
370  String query
371  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
372  + " FROM blackboard_artifacts " //NON-NLS
373  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
374  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
375  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
376  + " AND blackboard_attributes.value_text = '" + accountTypeName + "'" //NON-NLS
377  + getRejectedArtifactFilterClause(); //NON-NLS
378  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
379  ResultSet rs = results.getResultSet();) {
380  while (rs.next()) {
381  list.add(rs.getLong("artifact_id")); //NON-NLS
382  }
383  } catch (TskCoreException | SQLException ex) {
384  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
385  }
386  return list;
387  }
388 
389  @Override
390  protected Node[] createNodes(Long t) {
391  try {
392  return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))};
393  } catch (TskCoreException ex) {
394  LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
395  return new Node[0];
396  }
397  }
398 
399  @Subscribe
400  @Override
401  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
402  refreshKeys();
403  }
404 
405  @Subscribe
406  @Override
407  void handleDataAdded(ModuleDataEvent event) {
408  refreshKeys();
409  }
410  }
411 
412  private DefaultAccountTypeNode(String accountTypeName) {
413  super(Children.LEAF);
414  this.accountTypeName = accountTypeName;
415  setChildren(Children.createLazy(DefaultAccountFactory::new));
416  setName(accountTypeName);
417  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
418  }
419 
420  @Override
421  public boolean isLeafTypeNode() {
422  return true;
423  }
424 
425  @Override
426  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
427  return v.visit(this);
428  }
429 
430  @Override
431  public String getItemType() {
432  return getClass().getName();
433  }
434  }
435 
439  private enum CreditCardViewMode {
442  }
443 
448 
453  final private class ViewModeFactory extends ObservingChildren<CreditCardViewMode> {
454 
455  @Override
456  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
457  }
458 
459  @Override
460  void handleDataAdded(ModuleDataEvent event) {
461  }
462 
466  @Override
467  protected List<CreditCardViewMode> createKeys() {
468  return Arrays.asList(CreditCardViewMode.values());
469 
470  }
471 
472  @Override
473  protected Node[] createNodes(CreditCardViewMode key) {
474  switch (key) {
475  case BY_BIN:
476  return new Node[]{new ByBINNode()};
477  case BY_FILE:
478  return new Node[]{new ByFileNode()};
479  default:
480  return new Node[0];
481  }
482  }
483  }
484 
486  super(Children.LEAF);
487  setChildren(new ViewModeFactory());
488  setName(Account.Type.CREDIT_CARD.getDisplayName());
489  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
490  }
491 
492  @Override
493  public boolean isLeafTypeNode() {
494  return false;
495  }
496 
497  @Override
498  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
499  return v.visit(this);
500  }
501 
502  @Override
503  public String getItemType() {
504  return getClass().getName();
505  }
506  }
507 
512  final public class ByFileNode extends DisplayableItemNode {
513 
517  final private class FileWithCCNFactory extends ObservingChildren<FileWithCCN> {
518 
519  @Subscribe
520  @Override
521  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
522  refreshKeys();
523  }
524 
525  @Subscribe
526  @Override
527  void handleDataAdded(ModuleDataEvent event) {
528  refreshKeys();
529  }
530 
531  @Override
532  protected List<FileWithCCN> createKeys() {
533  List<FileWithCCN> list = new ArrayList<>();
534  String query
535  = "SELECT blackboard_artifacts.obj_id," //NON-NLS
536  + " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
537  if(skCase.getDatabaseType().equals(DbType.POSTGRESQL)){
538  query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
539  + " string_agg(blackboard_artifacts.review_status_id::character varying, ',') AS review_status_ids, ";
540  } else {
541  query += " GROUP_CONCAT(blackboard_artifacts.artifact_id) AS artifact_IDs, " //NON-NLS
542  + " GROUP_CONCAT(blackboard_artifacts.review_status_id) AS review_status_ids, ";
543  }
544  query += " COUNT( blackboard_artifacts.artifact_id) AS hits " //NON-NLS
545  + " FROM blackboard_artifacts " //NON-NLS
546  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
547  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
548  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
549  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
550  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.name() + "'" //NON-NLS
551  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
553  + " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
554  + " ORDER BY hits DESC "; //NON-NLS
555  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
556  ResultSet rs = results.getResultSet();) {
557  while (rs.next()) {
558  list.add(new FileWithCCN(
559  rs.getLong("obj_id"), //NON-NLS
560  rs.getString("solr_document_id"), //NON-NLS
561  unGroupConcat(rs.getString("artifact_IDs"), Long::valueOf), //NON-NLS
562  rs.getLong("hits"), //NON-NLS
563  new HashSet<>(unGroupConcat(rs.getString("review_status_ids"), id -> BlackboardArtifact.ReviewStatus.withID(Integer.valueOf(id)))))); //NON-NLS
564  }
565  } catch (TskCoreException | SQLException ex) {
566  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
567 
568  }
569  return list;
570  }
571 
572  @Override
573  protected Node[] createNodes(FileWithCCN key) {
574  //add all account artifacts for the file and the file itself to the lookup
575  try {
576  List<Object> lookupContents = new ArrayList<>();
577  for (long artId : key.artifactIDs) {
578  lookupContents.add(skCase.getBlackboardArtifact(artId));
579  }
580  AbstractFile abstractFileById = skCase.getAbstractFileById(key.getObjID());
581  lookupContents.add(abstractFileById);
582  return new Node[]{new FileWithCCNNode(key, abstractFileById, lookupContents.toArray())};
583  } catch (TskCoreException ex) {
584  LOGGER.log(Level.SEVERE, "Error getting content for file with ccn hits.", ex); //NON-NLS
585  return new Node[0];
586  }
587  }
588  }
589 
590  private ByFileNode() {
591  super(Children.LEAF);
592  setChildren(Children.createLazy(FileWithCCNFactory::new));
593  setName("By File"); //NON-NLS
594  updateDisplayName();
595  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
596  reviewStatusBus.register(this);
597  }
598 
599  @NbBundle.Messages({
600  "# {0} - number of children",
601  "Accounts.ByFileNode.displayName=By File ({0})"})
602  private void updateDisplayName() {
603  String query
604  = "SELECT count(*) FROM ( SELECT count(*) AS documents "
605  + " FROM blackboard_artifacts " //NON-NLS
606  + " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
607  + " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
608  + " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
609  + " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
610  + " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.name() + "'" //NON-NLS
611  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
613  + " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
614  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
615  ResultSet rs = results.getResultSet();) {
616  while (rs.next()) {
617  if(skCase.getDatabaseType().equals(DbType.POSTGRESQL)){
618  setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count")));
619  } else {
620  setDisplayName(Bundle.Accounts_ByFileNode_displayName(rs.getLong("count(*)")));
621  }
622  }
623  } catch (TskCoreException | SQLException ex) {
624  LOGGER.log(Level.SEVERE, "Error querying for files with ccn hits.", ex); //NON-NLS
625 
626  }
627  }
628 
629  @Override
630  public boolean isLeafTypeNode() {
631  return true;
632  }
633 
634  @Override
635  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
636  return v.visit(this);
637  }
638 
639  @Override
640  public String getItemType() {
641  return getClass().getName();
642  }
643 
644  @Subscribe
645  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
646  updateDisplayName();
647  }
648 
649  @Subscribe
650  void handleDataAdded(ModuleDataEvent event) {
651  updateDisplayName();
652  }
653  }
654 
659  final public class ByBINNode extends DisplayableItemNode {
660 
664  final private class BINFactory extends ObservingChildren<BinResult> {
665 
666  @Subscribe
667  @Override
668  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
669  refreshKeys();
670  }
671 
672  @Subscribe
673  @Override
674  void handleDataAdded(ModuleDataEvent event) {
675  refreshKeys();
676  }
677 
678  @Override
679  protected List<BinResult> createKeys() {
680  List<BinResult> list = new ArrayList<>();
681 
682  RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
683 
684  String query
685  = "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
686  + " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
687  + " FROM blackboard_artifacts " //NON-NLS
688  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
689  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
690  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
692  + " GROUP BY BIN " //NON-NLS
693  + " ORDER BY BIN "; //NON-NLS
694  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
695  ResultSet resultSet = results.getResultSet();
696  //sort all te individual bins in to the ranges
697  while (resultSet.next()) {
698  final Integer bin = Integer.valueOf(resultSet.getString("BIN"));
699  long count = resultSet.getLong("count");
700 
701  BINRange binRange = (BINRange) CreditCards.getBINInfo(bin);
702  BinResult previousResult = binRanges.get(bin);
703 
704  if (previousResult != null) {
705  binRanges.remove(Range.closed(previousResult.getBINStart(), previousResult.getBINEnd()));
706  count += previousResult.getCount();
707  }
708 
709  if (binRange != null) {
710  binRanges.put(Range.closed(binRange.getBINstart(), binRange.getBINend()), new BinResult(count, binRange));
711  } else {
712  binRanges.put(Range.closed(bin, bin), new BinResult(count, bin, bin));
713  }
714  }
715  binRanges.asMapOfRanges().values().forEach(list::add);
716  } catch (TskCoreException | SQLException ex) {
717  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
718 
719  }
720  return list;
721  }
722 
723  @Override
724  protected Node[] createNodes(BinResult key) {
725  return new Node[]{new BINNode(key)};
726  }
727  }
728 
729  @NbBundle.Messages("Accounts.ByBINNode.name=By BIN")
730  private ByBINNode() {
731  super(Children.LEAF);
732  setChildren(Children.createLazy(BINFactory::new));
733  setName(Bundle.Accounts_ByBINNode_name()); //NON-NLS
734  updateDisplayName();
735  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
736  reviewStatusBus.register(this);
737  }
738 
739  @NbBundle.Messages({
740  "# {0} - number of children",
741  "Accounts.ByBINNode.displayName=By BIN ({0})"})
742  private void updateDisplayName() {
743  String query
744  = "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
745  + " FROM blackboard_artifacts " //NON-NLS
746  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
747  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
748  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
749  + getRejectedArtifactFilterClause(); //NON-NLS
750  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query)) {
751  ResultSet resultSet = results.getResultSet();
752  while (resultSet.next()) {
753  setDisplayName(Bundle.Accounts_ByBINNode_displayName(resultSet.getLong("BINs")));
754  }
755  } catch (TskCoreException | SQLException ex) {
756  LOGGER.log(Level.SEVERE, "Error querying for BINs.", ex); //NON-NLS
757  }
758  }
759 
760  @Override
761  public boolean isLeafTypeNode() {
762  return false;
763  }
764 
765  @Override
766  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
767  return v.visit(this);
768  }
769 
770  @Override
771  public String getItemType() {
772  return getClass().getName();
773  }
774 
775  @Subscribe
776  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
777  updateDisplayName();
778  }
779 
780  @Subscribe
781  void handleDataAdded(ModuleDataEvent event) {
782  updateDisplayName();
783  }
784  }
785 
790  @Immutable
791  final private static class FileWithCCN {
792 
793  @Override
794  public int hashCode() {
795  int hash = 5;
796  hash = 79 * hash + (int) (this.objID ^ (this.objID >>> 32));
797  hash = 79 * hash + Objects.hashCode(this.keywordSearchDocID);
798  hash = 79 * hash + Objects.hashCode(this.artifactIDs);
799  hash = 79 * hash + (int) (this.hits ^ (this.hits >>> 32));
800  hash = 79 * hash + Objects.hashCode(this.statuses);
801  return hash;
802  }
803 
804  @Override
805  public boolean equals(Object obj) {
806  if (this == obj) {
807  return true;
808  }
809  if (obj == null) {
810  return false;
811  }
812  if (getClass() != obj.getClass()) {
813  return false;
814  }
815  final FileWithCCN other = (FileWithCCN) obj;
816  if (this.objID != other.objID) {
817  return false;
818  }
819  if (this.hits != other.hits) {
820  return false;
821  }
822  if (!Objects.equals(this.keywordSearchDocID, other.keywordSearchDocID)) {
823  return false;
824  }
825  if (!Objects.equals(this.artifactIDs, other.artifactIDs)) {
826  return false;
827  }
828  if (!Objects.equals(this.statuses, other.statuses)) {
829  return false;
830  }
831  return true;
832  }
833 
834  private final long objID;
835  private final String keywordSearchDocID;
836  private final List<Long> artifactIDs;
837  private final long hits;
838  private final Set<BlackboardArtifact.ReviewStatus> statuses;
839 
840  private FileWithCCN(long objID, String solrDocID, List<Long> artifactIDs, long hits, Set<BlackboardArtifact.ReviewStatus> statuses) {
841  this.objID = objID;
842  this.keywordSearchDocID = solrDocID;
843  this.artifactIDs = artifactIDs;
844  this.hits = hits;
845  this.statuses = statuses;
846  }
847 
853  public long getObjID() {
854  return objID;
855  }
856 
863  public String getkeywordSearchDocID() {
864  return keywordSearchDocID;
865  }
866 
872  public List<Long> getArtifactIDs() {
873  return artifactIDs;
874  }
875 
881  public long getHits() {
882  return hits;
883  }
884 
890  public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
891  return statuses;
892  }
893  }
894 
911  static <X> List<X> unGroupConcat(String groupConcat, Function<String, X> mapper) {
912  return StringUtils.isBlank(groupConcat) ? Collections.emptyList()
913  : Stream.of(groupConcat.split(",")) //NON-NLS
914  .map(mapper::apply)
915  .collect(Collectors.toList());
916  }
917 
921  final public class FileWithCCNNode extends DisplayableItemNode {
922 
923  private final FileWithCCN fileKey;
924  private final String fileName;
925 
935  @NbBundle.Messages({
936  "# {0} - raw file name",
937  "# {1} - solr chunk id",
938  "Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}"})
939  private FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents) {
940  super(Children.LEAF, Lookups.fixed(lookupContents));
941  this.fileKey = key;
942  this.fileName = (key.getkeywordSearchDocID() == null)
943  ? content.getName()
944  : Bundle.Accounts_FileWithCCNNode_unallocatedSpaceFile_displayName(content.getName(), StringUtils.substringAfter(key.getkeywordSearchDocID(), "_")); //NON-NLS
945  setName(fileName + key.getObjID());
946  setDisplayName(fileName);
947  }
948 
949  @Override
950  public boolean isLeafTypeNode() {
951  return true;
952  }
953 
954  @Override
955  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
956  return v.visit(this);
957  }
958 
959  @Override
960  public String getItemType() {
961  return getClass().getName();
962  }
963 
964  @Override
965  @NbBundle.Messages({
966  "Accounts.FileWithCCNNode.nameProperty.displayName=File",
967  "Accounts.FileWithCCNNode.accountsProperty.displayName=Accounts",
968  "Accounts.FileWithCCNNode.statusProperty.displayName=Status",
969  "Accounts.FileWithCCNNode.noDescription=no description"})
970  protected Sheet createSheet() {
971  Sheet s = super.createSheet();
972  Sheet.Set ss = s.get(Sheet.PROPERTIES);
973  if (ss == null) {
974  ss = Sheet.createPropertiesSet();
975  s.put(ss);
976  }
977 
978  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
979  Bundle.Accounts_FileWithCCNNode_nameProperty_displayName(),
980  Bundle.Accounts_FileWithCCNNode_noDescription(),
981  fileName));
982  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
983  Bundle.Accounts_FileWithCCNNode_accountsProperty_displayName(),
984  Bundle.Accounts_FileWithCCNNode_noDescription(),
985  fileKey.getHits()));
986  ss.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
987  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
988  Bundle.Accounts_FileWithCCNNode_noDescription(),
989  fileKey.getStatuses().stream()
990  .map(BlackboardArtifact.ReviewStatus::getDisplayName)
991  .collect(Collectors.joining(", ")))); //NON-NLS
992 
993  return s;
994  }
995 
996  @Override
997  public Action[] getActions(boolean context) {
998  Action[] actions = super.getActions(context);
999  ArrayList<Action> arrayList = new ArrayList<>();
1000  arrayList.addAll(Arrays.asList(actions));
1001  try {
1002  arrayList.addAll(DataModelActionsFactory.getActions(Accounts.this.skCase.getContentById(fileKey.getObjID()), false));
1003  } catch (TskCoreException ex) {
1004  LOGGER.log(Level.SEVERE, "Error gettung content by id", ex);
1005  }
1006 
1007  arrayList.add(approveActionInstance);
1008  arrayList.add(rejectActionInstance);
1009 
1010  return arrayList.toArray(new Action[arrayList.size()]);
1011  }
1012  }
1013 
1014  final public class BINNode extends DisplayableItemNode {
1015 
1019  final private class CreditCardNumberFactory extends ObservingChildren<Long> {
1020 
1021  @Subscribe
1022  @Override
1023  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1024  refreshKeys();
1025  //make sure to refresh the nodes for artifacts that changed statuses.
1026  event.artifacts.stream().map(BlackboardArtifact::getArtifactID).forEach(this::refreshKey);
1027  }
1028 
1029  @Subscribe
1030  @Override
1031  void handleDataAdded(ModuleDataEvent event) {
1032  refreshKeys();
1033  }
1034 
1035  @Override
1036  protected List<Long> createKeys() {
1037  List<Long> list = new ArrayList<>();
1038 
1039  String query
1040  = "SELECT blackboard_artifacts.artifact_id " //NON-NLS
1041  + " FROM blackboard_artifacts " //NON-NLS
1042  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1043  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1044  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1045  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1047  + " ORDER BY blackboard_attributes.value_text"; //NON-NLS
1048  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1049  ResultSet rs = results.getResultSet();) {
1050  while (rs.next()) {
1051  list.add(rs.getLong("artifact_id")); //NON-NLS
1052  }
1053  } catch (TskCoreException | SQLException ex) {
1054  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1055 
1056  }
1057  return list;
1058  }
1059 
1060  @Override
1061  protected Node[] createNodes(Long artifactID) {
1062  if (skCase == null) {
1063  return new Node[0];
1064  }
1065 
1066  try {
1067  BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID);
1068  return new Node[]{new AccountArtifactNode(art)};
1069  } catch (TskCoreException ex) {
1070  LOGGER.log(Level.WARNING, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS
1071  return new Node[0];
1072  }
1073  }
1074  }
1075  private final BinResult bin;
1076 
1077  private BINNode(BinResult bin) {
1078  super(Children.LEAF);
1079  this.bin = bin;
1080  setChildren(Children.createLazy(CreditCardNumberFactory::new));
1081  setName(getBinRangeString());
1082  updateDisplayName();
1083  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/bank.png"); //NON-NLS
1084  reviewStatusBus.register(this);
1085  }
1086 
1087  @Subscribe
1088  void handleReviewStatusChange(ReviewStatusChangeEvent event) {
1089  updateDisplayName();
1090  }
1091 
1092  @Subscribe
1093  void handleDataAdded(ModuleDataEvent event) {
1094  updateDisplayName();
1095  }
1096 
1097  private void updateDisplayName() {
1098  String query
1099  = "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
1100  + " FROM blackboard_artifacts " //NON-NLS
1101  + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
1102  + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
1103  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
1104  + " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
1106  try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
1107  ResultSet rs = results.getResultSet();) {
1108  while (rs.next()) {
1109  setDisplayName(getBinRangeString() + " (" + rs.getLong("count") + ")"); //NON-NLS
1110  }
1111  } catch (TskCoreException | SQLException ex) {
1112  LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
1113 
1114  }
1115 
1116  }
1117 
1118  private String getBinRangeString() {
1119  if (bin.getBINStart() == bin.getBINEnd()) {
1120  return Integer.toString(bin.getBINStart());
1121  } else {
1122  return bin.getBINStart() + "-" + StringUtils.difference(bin.getBINStart() + "", bin.getBINEnd() + "");
1123  }
1124  }
1125 
1126  @Override
1127  public boolean isLeafTypeNode() {
1128  return true;
1129  }
1130 
1131  @Override
1132  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
1133  return v.visit(this);
1134  }
1135 
1136  @Override
1137  public String getItemType() {
1138  return getClass().getName();
1139  }
1140 
1141  private Sheet.Set getPropertySet(Sheet s) {
1142  Sheet.Set ss = s.get(Sheet.PROPERTIES);
1143  if (ss == null) {
1144  ss = Sheet.createPropertiesSet();
1145  s.put(ss);
1146  }
1147  return ss;
1148  }
1149 
1150  @Override
1151  @NbBundle.Messages({
1152  "Accounts.BINNode.binProperty.displayName=Bank Identifier Number",
1153  "Accounts.BINNode.accountsProperty.displayName=Accounts",
1154  "Accounts.BINNode.cardTypeProperty.displayName=Payment Card Type",
1155  "Accounts.BINNode.schemeProperty.displayName=Credit Card Scheme",
1156  "Accounts.BINNode.brandProperty.displayName=Brand",
1157  "Accounts.BINNode.bankProperty.displayName=Bank",
1158  "Accounts.BINNode.bankCityProperty.displayName=Bank City",
1159  "Accounts.BINNode.bankCountryProperty.displayName=Bank Country",
1160  "Accounts.BINNode.bankPhoneProperty.displayName=Bank Phone #",
1161  "Accounts.BINNode.bankURLProperty.displayName=Bank URL",
1162  "Accounts.BINNode.noDescription=no description"})
1163  protected Sheet createSheet() {
1164  Sheet sheet = super.createSheet();
1165  Sheet.Set properties = getPropertySet(sheet);
1166 
1167  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_binProperty_displayName(),
1168  Bundle.Accounts_BINNode_binProperty_displayName(),
1169  Bundle.Accounts_BINNode_noDescription(),
1170  getBinRangeString()));
1171  properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_accountsProperty_displayName(),
1172  Bundle.Accounts_BINNode_accountsProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1173  bin.getCount()));
1174 
1175  //add optional properties if they are available
1176  if (bin.hasDetails()) {
1177  bin.getCardType().ifPresent(cardType -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_cardTypeProperty_displayName(),
1178  Bundle.Accounts_BINNode_cardTypeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1179  cardType)));
1180  bin.getScheme().ifPresent(scheme -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_schemeProperty_displayName(),
1181  Bundle.Accounts_BINNode_schemeProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1182  scheme)));
1183  bin.getBrand().ifPresent(brand -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_brandProperty_displayName(),
1184  Bundle.Accounts_BINNode_brandProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1185  brand)));
1186  bin.getBankName().ifPresent(bankName -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankProperty_displayName(),
1187  Bundle.Accounts_BINNode_bankProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1188  bankName)));
1189  bin.getBankCity().ifPresent(bankCity -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCityProperty_displayName(),
1190  Bundle.Accounts_BINNode_bankCityProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1191  bankCity)));
1192  bin.getCountry().ifPresent(country -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankCountryProperty_displayName(),
1193  Bundle.Accounts_BINNode_bankCountryProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1194  country)));
1195  bin.getBankPhoneNumber().ifPresent(phoneNumber -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankPhoneProperty_displayName(),
1196  Bundle.Accounts_BINNode_bankPhoneProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1197  phoneNumber)));
1198  bin.getBankURL().ifPresent(url -> properties.put(new NodeProperty<>(Bundle.Accounts_BINNode_bankURLProperty_displayName(),
1199  Bundle.Accounts_BINNode_bankURLProperty_displayName(), Bundle.Accounts_BINNode_noDescription(),
1200  url)));
1201  }
1202  return sheet;
1203  }
1204  }
1205 
1210  @Immutable
1211  final static private class BinResult implements CreditCards.BankIdentificationNumber {
1212 
1213  @Override
1214  public int hashCode() {
1215  int hash = 3;
1216  hash = 97 * hash + this.binEnd;
1217  hash = 97 * hash + this.binStart;
1218  return hash;
1219  }
1220 
1221  @Override
1222  public boolean equals(Object obj) {
1223  if (this == obj) {
1224  return true;
1225  }
1226  if (obj == null) {
1227  return false;
1228  }
1229  if (getClass() != obj.getClass()) {
1230  return false;
1231  }
1232  final BinResult other = (BinResult) obj;
1233  if (this.binEnd != other.binEnd) {
1234  return false;
1235  }
1236  if (this.binStart != other.binStart) {
1237  return false;
1238  }
1239  return true;
1240  }
1241 
1245  private final long count;
1246 
1247  private final BINRange binRange;
1248  private final int binEnd;
1249  private final int binStart;
1250 
1251  private BinResult(long count, @Nonnull BINRange binRange) {
1252  this.count = count;
1253  this.binRange = binRange;
1254  binStart = binRange.getBINstart();
1255  binEnd = binRange.getBINend();
1256  }
1257 
1258  private BinResult(long count, int start, int end) {
1259  this.count = count;
1260  this.binRange = null;
1261  binStart = start;
1262  binEnd = end;
1263  }
1264 
1265  int getBINStart() {
1266  return binStart;
1267  }
1268 
1269  int getBINEnd() {
1270  return binEnd;
1271  }
1272 
1273  long getCount() {
1274  return count;
1275  }
1276 
1277  boolean hasDetails() {
1278  return binRange != null;
1279  }
1280 
1281  @Override
1282  public Optional<Integer> getNumberLength() {
1283  return binRange.getNumberLength();
1284  }
1285 
1286  @Override
1287  public Optional<String> getBankCity() {
1288  return binRange.getBankCity();
1289  }
1290 
1291  @Override
1292  public Optional<String> getBankName() {
1293  return binRange.getBankName();
1294  }
1295 
1296  @Override
1297  public Optional<String> getBankPhoneNumber() {
1298  return binRange.getBankPhoneNumber();
1299  }
1300 
1301  @Override
1302  public Optional<String> getBankURL() {
1303  return binRange.getBankURL();
1304  }
1305 
1306  @Override
1307  public Optional<String> getBrand() {
1308  return binRange.getBrand();
1309  }
1310 
1311  @Override
1312  public Optional<String> getCardType() {
1313  return binRange.getCardType();
1314  }
1315 
1316  @Override
1317  public Optional<String> getCountry() {
1318  return binRange.getCountry();
1319  }
1320 
1321  @Override
1322  public Optional<String> getScheme() {
1323  return binRange.getScheme();
1324  }
1325  }
1326 
1327  final private class AccountArtifactNode extends BlackboardArtifactNode {
1328 
1329  private final BlackboardArtifact artifact;
1330 
1331  private AccountArtifactNode(BlackboardArtifact artifact) {
1332  super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS
1333  this.artifact = artifact;
1334  setName("" + this.artifact.getArtifactID());
1335  }
1336 
1337  @Override
1338  public Action[] getActions(boolean context) {
1339  List<Action> actionsList = new ArrayList<>();
1340  actionsList.addAll(Arrays.asList(super.getActions(context)));
1341 
1342  actionsList.add(approveActionInstance);
1343  actionsList.add(rejectActionInstance);
1344 
1345  return actionsList.toArray(new Action[actionsList.size()]);
1346  }
1347 
1348  @Override
1349  protected Sheet createSheet() {
1350  Sheet sheet = super.createSheet();
1351  Sheet.Set properties = sheet.get(Sheet.PROPERTIES);
1352  if (properties == null) {
1353  properties = Sheet.createPropertiesSet();
1354  sheet.put(properties);
1355  }
1356  properties.put(new NodeProperty<>(Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1357  Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(),
1358  Bundle.Accounts_FileWithCCNNode_noDescription(),
1359  artifact.getReviewStatus().getDisplayName()));
1360 
1361  return sheet;
1362  }
1363  }
1364 
1365  private final class ToggleShowRejected extends AbstractAction {
1366 
1367  @NbBundle.Messages("ToggleShowRejected.name=Show Rejected Results")
1368  ToggleShowRejected() {
1369  super(Bundle.ToggleShowRejected_name());
1370  }
1371 
1372  @Override
1373  public void actionPerformed(ActionEvent e) {
1374  showRejected = !showRejected;
1375  reviewStatusBus.post(new ReviewStatusChangeEvent(Collections.emptySet(), null));
1376  }
1377  }
1378 
1379  private abstract class ReviewStatusAction extends AbstractAction {
1380 
1381  private final BlackboardArtifact.ReviewStatus newStatus;
1382 
1383  private ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus) {
1384  super(displayName);
1385  this.newStatus = newStatus;
1386 
1387  }
1388 
1389  @Override
1390  public void actionPerformed(ActionEvent e) {
1391 
1392  /* get paths for selected nodes to reselect after applying review
1393  * status change */
1394  List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
1395  .map(node -> {
1396  String[] createPath;
1397  /*
1398  * If the we are rejecting and not showing rejected
1399  * results, then the selected node, won't exist any
1400  * more, so we select the previous one in stead.
1401  */
1402  if (newStatus == BlackboardArtifact.ReviewStatus.REJECTED && showRejected == false) {
1403  List<Node> siblings = Arrays.asList(node.getParentNode().getChildren().getNodes());
1404  if (siblings.size() > 1) {
1405  int indexOf = siblings.indexOf(node);
1406  //there is no previous for the first node, so instead we select the next one
1407  Node sibling = indexOf > 0
1408  ? siblings.get(indexOf - 1)
1409  : siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
1410  createPath = NodeOp.createPath(sibling, null);
1411  } else {
1412  /* if there are no other siblings to select,
1413  * just return null, but note we need to filter
1414  * this out of stream below */
1415  return null;
1416  }
1417  } else {
1418  createPath = NodeOp.createPath(node, null);
1419  }
1420  //for the reselect to work we need to strip off the first part of the path.
1421  return Arrays.copyOfRange(createPath, 1, createPath.length);
1422  })
1423  .filter(Objects::nonNull)
1424  .collect(Collectors.toList());
1425 
1426  //change status of selected artifacts
1427  final Collection<? extends BlackboardArtifact> artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
1428  artifacts.forEach(artifact -> {
1429  try {
1430  skCase.setReviewStatus(artifact, newStatus);
1431  } catch (TskCoreException ex) {
1432  LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
1433  }
1434  });
1435  //post event
1436  reviewStatusBus.post(new ReviewStatusChangeEvent(artifacts, newStatus));
1437 
1438  final DataResultTopComponent directoryListing = DirectoryTreeTopComponent.findInstance().getDirectoryListing();
1439  final Node rootNode = directoryListing.getRootNode();
1440 
1441  //convert paths back to nodes
1442  List<Node> toArray = new ArrayList<>();
1443  selectedPaths.forEach(path -> {
1444  try {
1445  toArray.add(NodeOp.findPath(rootNode, path));
1446  } catch (NodeNotFoundException ex) {
1447  //just ingnore paths taht don't exist. this is expected since we are rejecting
1448  }
1449  });
1450  //select nodes
1451  directoryListing.setSelectedNodes(toArray.toArray(new Node[toArray.size()]));
1452  }
1453  }
1454 
1455  final private class ApproveAccounts extends ReviewStatusAction {
1456 
1457  @NbBundle.Messages({"ApproveAccountsAction.name=Approve Accounts"})
1458  private ApproveAccounts() {
1459  super(Bundle.ApproveAccountsAction_name(), BlackboardArtifact.ReviewStatus.APPROVED);
1460  }
1461  }
1462 
1463  final private class RejectAccounts extends ReviewStatusAction {
1464 
1465  @NbBundle.Messages({"RejectAccountsAction.name=Reject Accounts"})
1466  private RejectAccounts() {
1467  super(Bundle.RejectAccountsAction_name(), BlackboardArtifact.ReviewStatus.REJECTED);
1468  }
1469  }
1470 
1471  private class ReviewStatusChangeEvent {
1472 
1473  Collection<? extends BlackboardArtifact> artifacts;
1474  BlackboardArtifact.ReviewStatus newReviewStatus;
1475 
1476  public ReviewStatusChangeEvent(Collection<? extends BlackboardArtifact> artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) {
1477  this.artifacts = artifacts;
1478  this.newReviewStatus = newReviewStatus;
1479  }
1480  }
1481 }
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
Set< BlackboardArtifact.ReviewStatus > getStatuses()
Definition: Accounts.java:890
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:838
void addIngestJobEventListener(final PropertyChangeListener listener)
ReviewStatusChangeEvent(Collection<?extends BlackboardArtifact > artifacts, BlackboardArtifact.ReviewStatus newReviewStatus)
Definition: Accounts.java:1476
BinResult(long count,@Nonnull BINRange binRange)
Definition: Accounts.java:1251
FileWithCCNNode(FileWithCCN key, Content content, Object[] lookupContents)
Definition: Accounts.java:939
FileWithCCN(long objID, String solrDocID, List< Long > artifactIDs, long hits, Set< BlackboardArtifact.ReviewStatus > statuses)
Definition: Accounts.java:840
void addIngestModuleEventListener(final PropertyChangeListener listener)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:395
ReviewStatusAction(String displayName, BlackboardArtifact.ReviewStatus newStatus)
Definition: Accounts.java:1383
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:440

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.