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

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