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

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