Merge branch 'release-4.12.0' of github.com:sleuthkit/autopsy into diagnostics_tool_5119

This commit is contained in:
Eugene Livis 2019-06-21 15:23:53 -04:00
commit d726b44f48
49 changed files with 1640 additions and 501 deletions

View File

@ -75,7 +75,7 @@ file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0
file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar
file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar
file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar
file.reference.sleuthkit-postgresql-4.6.6.jar=release/modules/ext/sleuthkit-postgresql-4.6.6.jar file.reference.sleuthkit-postgresql-4.6.7.jar=release/modules/ext/sleuthkit-postgresql-4.6.7.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar

View File

@ -482,8 +482,8 @@
<binary-origin>release\modules\ext\jmatio-1.5.jar</binary-origin> <binary-origin>release\modules\ext\jmatio-1.5.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.6.jar</runtime-relative-path> <runtime-relative-path>ext/sleuthkit-postgresql-4.6.7.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.6.jar</binary-origin> <binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.7.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/tika-parsers-1.20.jar</runtime-relative-path> <runtime-relative-path>ext/tika-parsers-1.20.jar</runtime-relative-path>

View File

@ -175,7 +175,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text()); org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text());
addItemToBag(gridY, 0, 0, 0, knownStatusLabel); addItemToBag(gridY, 0, 0, 0, knownStatusLabel);
javax.swing.JLabel knownStatusValue = new javax.swing.JLabel(); javax.swing.JLabel knownStatusValue = new javax.swing.JLabel();
knownStatusValue.setText(knownStatus.toString()); knownStatusValue.setText(knownStatus.getName());
if (knownStatus == TskData.FileKnown.BAD) { if (knownStatus == TskData.FileKnown.BAD) {
knownStatusValue.setForeground(Color.RED); knownStatusValue.setForeground(Color.RED);
} }

View File

@ -66,7 +66,7 @@
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="5" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
@ -95,7 +95,7 @@
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/> <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
@ -464,7 +464,7 @@
<Container class="javax.swing.JPanel" name="accountTypeListPane"> <Container class="javax.swing.JPanel" name="accountTypeListPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/> <Property name="axis" type="int" value="3"/>
</Layout> </Layout>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -53,6 +53,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent.COMPLETED;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -63,6 +64,7 @@ import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter; import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
import static org.sleuthkit.datamodel.Relationship.Type.CONTACT; import static org.sleuthkit.datamodel.Relationship.Type.CONTACT;
@ -96,9 +98,10 @@ final public class FiltersPanel extends JPanel {
* Listens to ingest events to enable refresh button * Listens to ingest events to enable refresh button
*/ */
private final PropertyChangeListener ingestListener; private final PropertyChangeListener ingestListener;
private final PropertyChangeListener ingestJobListener;
/** /**
* Flag that indicates the UI is not up-sto-date with respect to the case DB * Flag that indicates the UI is not up-to-date with respect to the case DB
* and it should be refreshed (by reapplying the filters). * and it should be refreshed (by reapplying the filters).
*/ */
private boolean needsRefresh; private boolean needsRefresh;
@ -123,6 +126,11 @@ final public class FiltersPanel extends JPanel {
@NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"})
public FiltersPanel() { public FiltersPanel() {
initComponents(); initComponents();
CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(Account.Type.DEVICE, true);
accountTypeMap.put(Account.Type.DEVICE, panel.getCheckBox());
accountTypeListPane.add(panel);
deviceRequiredLabel.setVisible(false); deviceRequiredLabel.setVisible(false);
accountTypeRequiredLabel.setVisible(false); accountTypeRequiredLabel.setVisible(false);
startDatePicker.setDate(LocalDate.now().minusWeeks(3)); startDatePicker.setDate(LocalDate.now().minusWeeks(3));
@ -155,18 +163,30 @@ final public class FiltersPanel extends JPanel {
// Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
if (null != eventData if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
&& eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
updateFilters(false); || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()))
{
updateFilters(true);
needsRefresh = true; needsRefresh = true;
validateFilters(); validateFilters();
} }
} }
}; };
this.ingestJobListener = pce -> {
String eventType = pce.getPropertyName();
if (eventType.equals(COMPLETED.toString()) &&
updateFilters(true)) {
needsRefresh = true;
validateFilters();
}
};
applyFiltersButton.addActionListener(e -> applyFilters()); applyFiltersButton.addActionListener(e -> applyFilters());
refreshButton.addActionListener(e -> applyFilters()); refreshButton.addActionListener(e -> applyFilters());
} }
/** /**
@ -219,19 +239,26 @@ final public class FiltersPanel extends JPanel {
/** /**
* Updates the filter widgets to reflect he data sources/types in the case. * Updates the filter widgets to reflect he data sources/types in the case.
*/ */
private void updateFilters(boolean initialState) { private boolean updateFilters(boolean initialState) {
updateAccountTypeFilter(); boolean newAccountType = updateAccountTypeFilter(initialState);
updateDeviceFilter(initialState); boolean newDeviceFilter = updateDeviceFilter(initialState);
// both or either are true, return true;
return newAccountType || newDeviceFilter;
} }
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
IngestManager.getInstance().addIngestModuleEventListener(ingestListener); IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
IngestManager.getInstance().addIngestJobEventListener(ingestJobListener);
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
//clear the device filter widget when the case changes. //clear the device filter widget when the case changes.
devicesMap.clear(); devicesMap.clear();
devicesListPane.removeAll(); devicesListPane.removeAll();
accountTypeMap.clear();
accountTypeListPane.removeAll();
}); });
} }
@ -239,64 +266,107 @@ final public class FiltersPanel extends JPanel {
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
} }
/** /**
* Populate the Account Types filter widgets * Populate the Account Types filter widgets
*
* @param selected the initial value for the account type checkbox
*
* @return True, if a new accountType was found
*/ */
private void updateAccountTypeFilter() { private boolean updateAccountTypeFilter(boolean selected) {
boolean newOneFound = false;
try {
final CommunicationsManager communicationsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
List<Account.Type> accountTypesInUse = communicationsManager.getAccountTypesInUse();
//TODO: something like this commented code could be used to show only for (Account.Type type : accountTypesInUse) {
//the account types that are found:
//final CommunicationsManager communicationsManager = Case.getCurrentOpenCase().getSleuthkitCase().getCommunicationsManager();
//List<Account.Type> accountTypesInUse = communicationsManager.getAccountTypesInUse();
//accountTypesInUSe.forEach(...)
Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> {
if (type.equals(Account.Type.CREDIT_CARD)) {
//don't show a check box for credit cards
} else {
accountTypeMap.computeIfAbsent(type, t -> {
if (!accountTypeMap.containsKey(type) && !type.equals(Account.Type.CREDIT_CARD)) {
CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(type, selected);
accountTypeMap.put(type, panel.getCheckBox());
accountTypeListPane.add(panel);
newOneFound = true;
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update to update Account Types Filter", ex);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "A case is required to update the account types filter.", ex);
}
if (newOneFound) {
accountTypeListPane.revalidate();
}
return newOneFound;
}
/**
* Helper function to create a new instance of the CheckBoxIconPanel base on
* the Account.Type and initalState (check box state).
*
* @param type Account.Type to display on the panel
* @param initalState initial check box state
*
* @return instance of the CheckBoxIconPanel
*/
private CheckBoxIconPanel createAccoutTypeCheckBoxPanel(Account.Type type, boolean initalState) {
CheckBoxIconPanel panel = new CheckBoxIconPanel( CheckBoxIconPanel panel = new CheckBoxIconPanel(
type.getDisplayName(), type.getDisplayName(),
new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type)))); new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type))));
panel.setSelected(true);
panel.setSelected(initalState);
panel.addItemListener(validationListener); panel.addItemListener(validationListener);
accountTypeListPane.add(panel); if (type.equals(Account.Type.DEVICE)) {
if (t.equals(Account.Type.DEVICE)) {
//Deveice type filter is enabled based on whether we are in table or graph view. //Deveice type filter is enabled based on whether we are in table or graph view.
panel.setEnabled(deviceAccountTypeEnabled); panel.setEnabled(deviceAccountTypeEnabled);
} }
return panel.getCheckBox();
}); return panel;
}
});
} }
/** /**
* Populate the devices filter widgets * Populate the devices filter widgets
* *
* @param initialState * @param selected Sets the initial state of device check box
*
* @return true if a new device was found
*/ */
private void updateDeviceFilter(boolean initialState) { private boolean updateDeviceFilter(boolean selected) {
boolean newOneFound = false;
try { try {
final SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase(); final SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase();
for (DataSource dataSource : sleuthkitCase.getDataSources()) { for (DataSource dataSource : sleuthkitCase.getDataSources()) {
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
//store the device id in the map, but display a datasource name in the UI. if(devicesMap.containsKey(dataSource.getDeviceId())) {
devicesMap.computeIfAbsent(dataSource.getDeviceId(), ds -> { continue;
final JCheckBox jCheckBox = new JCheckBox(dsName, initialState); }
final JCheckBox jCheckBox = new JCheckBox(dsName, selected);
jCheckBox.addItemListener(validationListener); jCheckBox.addItemListener(validationListener);
devicesListPane.add(jCheckBox); devicesListPane.add(jCheckBox);
return jCheckBox; devicesMap.put(dataSource.getDeviceId(), jCheckBox);
});
newOneFound = true;
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.INFO, "Filter update cancelled. Case is closed."); logger.log(Level.INFO, "Filter update cancelled. Case is closed.");
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException); logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
} }
if(newOneFound) {
devicesListPane.revalidate();
}
return newOneFound;
} }
/** /**
@ -366,6 +436,12 @@ final public class FiltersPanel extends JPanel {
endDatePicker.setEnabled(state.isEnabled()); endDatePicker.setEnabled(state.isEnabled());
} }
/**
* Sets the state of the most recent UI controls based on the current values
* in MostRecentFilter.
*
* @param filter The MostRecentFilter state to be set
*/
private void setMostRecentFilter(MostRecentFilter filter) { private void setMostRecentFilter(MostRecentFilter filter) {
int limit = filter.getLimit(); int limit = filter.getLimit();
if(limit > 0) { if(limit > 0) {
@ -424,6 +500,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.gridx = 1; gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5);
topPane.add(applyFiltersButton, gridBagConstraints); topPane.add(applyFiltersButton, gridBagConstraints);
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
@ -697,7 +774,7 @@ final public class FiltersPanel extends JPanel {
accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200)); accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200));
accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS)); accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.PAGE_AXIS));
accountTypesScrollPane.setViewportView(accountTypeListPane); accountTypesScrollPane.setViewportView(accountTypeListPane);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
@ -740,6 +817,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0; gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0; gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
add(scrollPane, gridBagConstraints); add(scrollPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -808,6 +886,11 @@ final public class FiltersPanel extends JPanel {
endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0); endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0);
} }
/**
* Get a MostRecentFilter that based on the current state of the ui controls.
*
* @return A new instance of MostRecentFilter
*/
private MostRecentFilter getMostRecentFilter() { private MostRecentFilter getMostRecentFilter() {
String value = (String)limitComboBox.getSelectedItem(); String value = (String)limitComboBox.getSelectedItem();
if(value.trim().equalsIgnoreCase("all")){ if(value.trim().equalsIgnoreCase("all")){

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View File

@ -11,5 +11,13 @@ SummaryViewer.emailDataLabel.text=emails
SummaryViewer.attachmentsDataLabel.text=attachments SummaryViewer.attachmentsDataLabel.text=attachments
SummaryViewer.messagesLabel.text=Messages: SummaryViewer.messagesLabel.text=Messages:
SummaryViewer.callLogsLabel.text=Call Logs: SummaryViewer.callLogsLabel.text=Call Logs:
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
MessageViewer.backButton.AccessibleContext.accessibleDescription=
MessageViewer.backButton.text=Threads
MessageViewer.showAllButton.text=All Messages

View File

@ -19,12 +19,16 @@ MessageNode_Node_Property_Subject=Subject
MessageNode_Node_Property_To=To MessageNode_Node_Property_To=To
MessageNode_Node_Property_Type=Type MessageNode_Node_Property_Type=Type
MessageViewer_columnHeader_Attms=Attachments MessageViewer_columnHeader_Attms=Attachments
MessageViewer_columnHeader_Date=Date MessageViewer_columnHeader_Date=Data
MessageViewer_columnHeader_EarlyDate=Earliest Message
MessageViewer_columnHeader_From=From MessageViewer_columnHeader_From=From
MessageViewer_columnHeader_Subject=Subject MessageViewer_columnHeader_Subject=Subject
MessageViewer_columnHeader_To=To MessageViewer_columnHeader_To=To
MessageViewer_no_messages=<No messages found for selected account> MessageViewer_no_messages=<No messages found for selected account>
MessageViewer_tabTitle=Messages MessageViewer_tabTitle=Messages
MessageViewer_viewMessage_all=All
MessageViewer_viewMessage_selected=Selected
MessageViewer_viewMessage_unthreaded=Unthreaded
SummaryViewer.countsPanel.border.title=Counts SummaryViewer.countsPanel.border.title=Counts
SummaryViewer.emailLabel.text=Emails: SummaryViewer.emailLabel.text=Emails:
SummaryViewer.contactsLabel.text=Contacts: SummaryViewer.contactsLabel.text=Contacts:
@ -37,11 +41,19 @@ SummaryViewer.emailDataLabel.text=emails
SummaryViewer.attachmentsDataLabel.text=attachments SummaryViewer.attachmentsDataLabel.text=attachments
SummaryViewer.messagesLabel.text=Messages: SummaryViewer.messagesLabel.text=Messages:
SummaryViewer.callLogsLabel.text=Call Logs: SummaryViewer.callLogsLabel.text=Call Logs:
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
SummaryViewer_CaseRefNameColumn_Title=Case Name SummaryViewer_CaseRefNameColumn_Title=Case Name
SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Other Occurrences> SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Other Occurrences>
SummaryViewer_Creation_Date_Title=Creation Date SummaryViewer_Creation_Date_Title=Creation Date
SummaryViewer_FileRefNameColumn_Title=Path SummaryViewer_FileRefNameColumn_Title=Path
SummaryViewer_TabTitle=Summary SummaryViewer_TabTitle=Summary
SummeryViewer_FileRef_Message=<Select one Accout to see File References> SummeryViewer_FileRef_Message=<Select one Accout to see File References>
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
MessageViewer.backButton.AccessibleContext.accessibleDescription=
MessageViewer.backButton.text=Threads
MessageViewer.showAllButton.text=All Messages

View File

@ -17,34 +17,7 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues> </AuxValues>
<Layout> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="propertySheet" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="propertySheet" pref="283" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="nameLabel"> <Component class="javax.swing.JLabel" name="nameLabel">
<Properties> <Properties>
@ -55,11 +28,21 @@
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="ContactDetailsPane.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="ContactDetailsPane.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="16" insetsLeft="15" insetsBottom="15" insetsRight="15" anchor="23" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component> </Component>
<Component class="org.openide.explorer.propertysheet.PropertySheet" name="propertySheet"> <Component class="org.openide.explorer.propertysheet.PropertySheet" name="propertySheet">
<Properties> <Properties>
<Property name="descriptionAreaVisible" type="boolean" value="false"/> <Property name="descriptionAreaVisible" type="boolean" value="false"/>
</Properties> </Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="15" insetsBottom="16" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component> </Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -73,38 +73,36 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer(); messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer();
nameLabel = new javax.swing.JLabel(); nameLabel = new javax.swing.JLabel();
propertySheet = new org.openide.explorer.propertysheet.PropertySheet(); propertySheet = new org.openide.explorer.propertysheet.PropertySheet();
setLayout(new java.awt.GridBagLayout());
nameLabel.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N nameLabel.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(16, 15, 15, 15);
add(nameLabel, gridBagConstraints);
propertySheet.setDescriptionAreaVisible(false); propertySheet.setDescriptionAreaVisible(false);
gridBagConstraints = new java.awt.GridBagConstraints();
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); gridBagConstraints.gridx = 0;
this.setLayout(layout); gridBagConstraints.gridy = 1;
layout.setHorizontalGroup( gridBagConstraints.gridwidth = 2;
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
.addGroup(layout.createSequentialGroup() gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
.addContainerGap() gridBagConstraints.weightx = 1.0;
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) gridBagConstraints.weighty = 1.0;
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) gridBagConstraints.insets = new java.awt.Insets(9, 15, 16, 15);
.addGroup(layout.createSequentialGroup() add(propertySheet, gridBagConstraints);
.addComponent(nameLabel)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(nameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents

View File

@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* ChildFactory for ContactNodes. * ChildFactory for ContactNodes.
*/ */
final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{ final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName()); private static final Logger logger = Logger.getLogger(ContactsChildNodeFactory.class.getName());
private SelectionInfo selectionInfo; private SelectionInfo selectionInfo;

View File

@ -130,9 +130,7 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
logger.log(Level.WARNING, "Unable to update selection." , ex); logger.log(Level.WARNING, "Unable to update selection." , ex);
} }
if(artifactList.size() == 0) {
thumbnailViewer.resetComponent(); thumbnailViewer.resetComponent();
}
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentsChildren(artifactList)), tableEM), true, this.getClass().getName())); thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentsChildren(artifactList)), tableEM), true, this.getClass().getName()));
} }

View File

@ -20,11 +20,11 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -40,20 +40,31 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU
import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.communications.Utils; import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
/** /**
* Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView * Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView
*/ */
final class MessageNode extends BlackboardArtifactNode { class MessageNode extends BlackboardArtifactNode {
public static final String UNTHREADED_ID = "<UNTHREADED>";
private static final Logger logger = Logger.getLogger(MessageNode.class.getName()); private static final Logger logger = Logger.getLogger(MessageNode.class.getName());
MessageNode(BlackboardArtifact artifact) { private final String threadID;
private final Action preferredAction;
MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(artifact); super(artifact);
this.preferredAction = preferredAction;
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase); setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
this.threadID = threadID;
} }
@Messages({ @Messages({
@ -76,6 +87,8 @@ final class MessageNode extends BlackboardArtifactNode {
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS
final BlackboardArtifact artifact = getArtifact(); final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
@ -167,4 +180,13 @@ final class MessageNode extends BlackboardArtifactNode {
public String getSourceName() { public String getSourceName() {
return getDisplayName(); return getDisplayName();
} }
String getThreadID() {
return threadID;
}
@Override
public Action getPreferredAction() {
return preferredAction;
}
} }

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,-126,0,0,2,-76"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="rootMessagesPane">
<Properties>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="threads"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="threadsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.threadsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="15" insetsBottom="9" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="showAllButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.showAllButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showAllButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="15" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="rootTablePane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder/>
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="9" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="messagePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="messages"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.MessagesPanel" name="threadMessagesPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessagesPanel()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="0" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="backButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.backButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.backButton.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="backButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="9" insetsRight="15" anchor="13" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="showingMessagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.showingMessagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="15" insetsBottom="5" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="threadNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.threadNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="5" insetsBottom="5" insetsRight="15" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,439 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.KeyboardFocusManager;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* The main panel for the messages tab of the RelationshipViewer
*
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class MessageViewer extends JPanel implements RelationshipsViewer {
private static final Logger logger = Logger.getLogger(MessageViewer.class.getName());
private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private final ThreadChildNodeFactory rootMessageFactory;
private final MessagesChildNodeFactory threadMessageNodeFactory;
private SelectionInfo currentSelectionInfo = null;
OutlineViewPanel currentPanel;
@Messages({
"MessageViewer_tabTitle=Messages",
"MessageViewer_columnHeader_From=From",
"MessageViewer_columnHeader_Date=Data",
"MessageViewer_columnHeader_To=To",
"MessageViewer_columnHeader_EarlyDate=Earliest Message",
"MessageViewer_columnHeader_Subject=Subject",
"MessageViewer_columnHeader_Attms=Attachments",
"MessageViewer_no_messages=<No messages found for selected account>",
"MessageViewer_viewMessage_all=All",
"MessageViewer_viewMessage_selected=Selected",
"MessageViewer_viewMessage_unthreaded=Unthreaded",})
/**
* Creates new form MessageViewer
*/
public MessageViewer() {
initComponents();
currentPanel = rootTablePane;
proxyLookup = new ModifiableProxyLookup(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction());
threadMessageNodeFactory = new MessagesChildNodeFactory();
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessageViewer.this)) {
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
}
}
};
rootTablePane.getExplorerManager().setRootContext(
new AbstractNode(Children.create(rootMessageFactory, true)));
rootTablePane.getOutlineView().setPopupAllowed(false);
Outline outline = rootTablePane.getOutlineView().getOutline();
rootTablePane.getOutlineView().setPropertyColumns(
"Date", Bundle.MessageViewer_columnHeader_EarlyDate(),
"Subject", Bundle.MessageViewer_columnHeader_Subject()
);
outline.setRootVisible(false);
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel("Type");
outline.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
rootTablePane.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
showSelectedThread();
}
});
threadMessagesPanel.setChildFactory(threadMessageNodeFactory);
rootTablePane.setTableColumnsWidth(10, 20, 70);
Image image = getScaledImage((new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/timeline/images/arrow-180.png"))).getImage(), 16, 16);
backButton.setIcon(new ImageIcon(image) );
}
@Override
public String getDisplayName() {
return Bundle.MessageViewer_tabTitle();
}
@Override
public JPanel getPanel() {
return this;
}
@Override
public void setSelectionInfo(SelectionInfo info) {
currentSelectionInfo = info;
currentPanel = rootTablePane;
CardLayout layout = (CardLayout) this.getLayout();
layout.show(this, "threads");
rootMessageFactory.refresh(info);
}
@Override
public Lookup getLookup() {
return proxyLookup;
}
@Override
public void addNotify() {
super.addNotify();
//add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener);
}
@Override
public void removeNotify() {
super.removeNotify();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener);
}
@SuppressWarnings("rawtypes")
private void showSelectedThread() {
final Node[] nodes = rootTablePane.getExplorerManager().getSelectedNodes();
if (nodes == null) {
return;
}
if (nodes.length == 0 || nodes.length > 1) {
return;
}
ArrayList<String> threadIDList = new ArrayList<>();
String subject = "";
PropertySet[] propertySets = nodes[0].getPropertySets();
for (PropertySet pset : propertySets) {
Property[] properties = pset.getProperties();
for (Property prop : properties) {
if (prop.getName().equalsIgnoreCase("threadid")) {
try {
String threadID = prop.getValue().toString();
if (!threadIDList.contains(threadID)) {
threadIDList.add(threadID);
}
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, String.format("Unable to get threadid for node: %s", nodes[0].getDisplayName()), ex);
}
} else if (prop.getName().equalsIgnoreCase("subject")) {
try {
subject = prop.getValue().toString();
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, String.format("Unable to get subject for node: %s", nodes[0].getDisplayName()), ex);
subject = "<unavailable>";
}
}
}
}
if (!threadIDList.isEmpty()) {
threadMessageNodeFactory.refresh(currentSelectionInfo, threadIDList);
if (!subject.isEmpty()) {
threadNameLabel.setText(subject);
} else {
threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded());
}
showMessagesPane();
}
}
/**
* Make the threads pane visible.
*/
private void showThreadsPane() {
switchCard("threads");
}
/**
* Make the message pane visible.
*/
private void showMessagesPane() {
switchCard("messages");
}
/**
* Changes the visible panel (card).
*
* @param cardName Name of card to show
*/
private void switchCard(String cardName) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CardLayout layout = (CardLayout)getLayout();
layout.show(MessageViewer.this, cardName);
}
});
}
/**
* Scales the given image to the given width and height.
*
* @param srcImg Image to scale
* @param w Image width
* @param h Image height
*
* @return Scaled version of srcImg
*/
private Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.'
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
rootMessagesPane = new javax.swing.JPanel();
threadsLabel = new javax.swing.JLabel();
showAllButton = new javax.swing.JButton();
rootTablePane = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messagePanel = new javax.swing.JPanel();
threadMessagesPanel = new MessagesPanel();
backButton = new javax.swing.JButton();
showingMessagesLabel = new javax.swing.JLabel();
threadNameLabel = new javax.swing.JLabel();
setLayout(new java.awt.CardLayout());
rootMessagesPane.setOpaque(false);
rootMessagesPane.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(threadsLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 15, 9, 0);
rootMessagesPane.add(threadsLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(showAllButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showAllButton.text")); // NOI18N
showAllButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showAllButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 15, 0);
rootMessagesPane.add(showAllButton, gridBagConstraints);
rootTablePane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 9, 15);
rootMessagesPane.add(rootTablePane, gridBagConstraints);
add(rootMessagesPane, "threads");
messagePanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15);
messagePanel.add(threadMessagesPanel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.text")); // NOI18N
backButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
backButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 15);
messagePanel.add(backButton, gridBagConstraints);
backButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.AccessibleContext.accessibleDescription")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(showingMessagesLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showingMessagesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(9, 15, 5, 0);
messagePanel.add(showingMessagesLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(threadNameLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadNameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(9, 5, 5, 15);
messagePanel.add(threadNameLabel, gridBagConstraints);
add(messagePanel, "messages");
}// </editor-fold>//GEN-END:initComponents
private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
try {
rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
} catch (PropertyVetoException ex) {
Exceptions.printStackTrace(ex);
}
showThreadsPane();
}//GEN-LAST:event_backButtonActionPerformed
private void showAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAllButtonActionPerformed
threadMessageNodeFactory.refresh(currentSelectionInfo, null);
threadNameLabel.setText("All Messages");
showMessagesPane();
}//GEN-LAST:event_showAllButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton;
private javax.swing.JPanel messagePanel;
private javax.swing.JPanel rootMessagesPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel rootTablePane;
private javax.swing.JButton showAllButton;
private javax.swing.JLabel showingMessagesLabel;
private org.sleuthkit.autopsy.communications.relationships.MessagesPanel threadMessagesPanel;
private javax.swing.JLabel threadNameLabel;
private javax.swing.JLabel threadsLabel;
// End of variables declaration//GEN-END:variables
/**
* The preferred action of the table nodes.
*/
class ShowThreadMessagesAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
showSelectedThread();
}
});
}
}
}

View File

@ -27,52 +27,51 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* ChildFactory that creates createKeys and nodes from a given selectionInfo for * A ChildFactory subclass for creating MessageNodes from a set of
* only emails, call logs and messages. * BlackboardArtifact objects.
* *
*/ */
final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> { public class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact>{
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName()); private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName());
private SelectionInfo selectionInfo; private SelectionInfo selectionInfo;
/** private List<String> threadIDs;
* Construct a new MessageChildNodeFactory from the currently selectionInfo
* MessagesChildNodeFactory(SelectionInfo selectionInfo, List<String> threadIDs) {
* @param selectionInfo SelectionInfo object for the currently selected
* accounts
*/
MessagesChildNodeFactory(SelectionInfo selectionInfo) {
this.selectionInfo = selectionInfo; this.selectionInfo = selectionInfo;
this.threadIDs = threadIDs;
}
MessagesChildNodeFactory() {
this(null, null);
} }
/** /**
* Updates the current instance of selectionInfo and calls the refresh method. * Updates the current instance of selectionInfo and calls the refresh method.
* *
* @param selectionInfo New instance of the currently selected accounts * @param selectionInfo New instance of the currently selected accounts
* @param threadIDs A list of threadIDs to filter the keys by, null will
* return all keys for the selected accounts.
*/ */
public void refresh(SelectionInfo selectionInfo) { public void refresh(SelectionInfo selectionInfo, List<String> threadIDs) {
this.threadIDs = threadIDs;
this.selectionInfo = selectionInfo; this.selectionInfo = selectionInfo;
refresh(true); refresh(true);
} }
/**
* Creates a list of Keys (BlackboardArtifact) for only messages for the
* currently selected accounts
*
* @param list List of BlackboardArtifact to populate
*
* @return True on success
*/
@Override @Override
protected boolean createKeys(List<BlackboardArtifact> list) { protected boolean createKeys(List<BlackboardArtifact> list) {
CommunicationsManager communicationManager; CommunicationsManager communicationManager;
try { try {
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
} catch (NoCurrentCaseException | TskCoreException ex) { } catch (NoCurrentCaseException | TskCoreException ex) {
@ -87,19 +86,37 @@ final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
final Set<Content> relationshipSources; final Set<Content> relationshipSources;
try { try {
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
for(Content content: relationshipSources) {
if( !(content instanceof BlackboardArtifact)){
continue;
}
BlackboardArtifact bba = (BlackboardArtifact) content; BlackboardArtifact bba = (BlackboardArtifact) content;
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG if (fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG && fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) { && fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
continue;
}
// We want all artifacts that do not have "threadIDs" to appear as one thread in the UI
// To achive this assign any artifact that does not have a threadID
// the "UNTHREADED_ID"
String artifactThreadID = MessageNode.UNTHREADED_ID;
BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
if(attribute != null) {
artifactThreadID = attribute.getValueString();
}
if(threadIDs == null || threadIDs.contains(artifactThreadID)) {
list.add(bba); list.add(bba);
} }
});
}
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
@ -110,6 +127,7 @@ final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
@Override @Override
protected Node createNodeForKey(BlackboardArtifact key) { protected Node createNodeForKey(BlackboardArtifact key) {
return new MessageNode(key); return new MessageNode(key, null, null);
} }
} }

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -11,33 +11,23 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,13,0,0,2,103"/> <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JSplitPane" name="splitPane"> <Container class="javax.swing.JSplitPane" name="splitPane">
<Properties> <Properties>
<Property name="orientation" type="int" value="0"/> <Property name="orientation" type="int" value="0"/>
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/> <BorderConstraints direction="Center"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="bottom"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel"> <Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
@ -45,6 +35,16 @@
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="messageContentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -22,57 +22,38 @@ import java.awt.Component;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom; import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline; import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup; import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeMemberEvent;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/** /**
* Visualation for the messages of the currently selected accounts. *
* General Purpose class for panels that need OutlineView of message nodes at
* the top with a MessageContentViewer at the bottom.
*/ */
@ServiceProvider(service = RelationshipsViewer.class) public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
public final class MessagesViewer extends JPanel implements RelationshipsViewer {
private final Outline outline; private final Outline outline;
private final ModifiableProxyLookup proxyLookup; private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener; private final PropertyChangeListener focusPropertyListener;
private final MessagesChildNodeFactory nodeFactory;
@Messages({
"MessageViewer_tabTitle=Messages",
"MessageViewer_columnHeader_From=From",
"MessageViewer_columnHeader_To=To",
"MessageViewer_columnHeader_Date=Date",
"MessageViewer_columnHeader_Subject=Subject",
"MessageViewer_columnHeader_Attms=Attachments",
"MessageViewer_no_messages=<No messages found for selected account>"
})
/** /**
* Visualation for the messages of the currently selected accounts. * Creates new form MessagesPanel
*/ */
public MessagesViewer() { public MessagesPanel() {
initComponents(); initComponents();
splitPane.setResizeWeight(0.5);
outlineViewPanel.hideOutlineView(Bundle.MessageViewer_no_messages());
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap())); proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
nodeFactory = new MessagesChildNodeFactory(null);
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed // See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener // explaination of focusPropertyListener
@ -83,10 +64,10 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
if (newFocusOwner == null) { if (newFocusOwner == null) {
return; return;
} }
if (isDescendingFrom(newFocusOwner, contentViewer)) { if (isDescendingFrom(newFocusOwner, messageContentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table) //if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap())); proxyLookup.setNewLookups(createLookup(((MessageDataContent) messageContentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessagesViewer.this)) { } else if (isDescendingFrom(newFocusOwner, MessagesPanel.this)) {
//... or if it is within the Results table. //... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap())); proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
@ -110,52 +91,22 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes(); final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes();
if (nodes != null && nodes.length == 1) { if (nodes != null && nodes.length == 1) {
contentViewer.setNode(nodes[0]); messageContentViewer.setNode(nodes[0]);
} }
else { else {
contentViewer.setNode(null); messageContentViewer.setNode(null);
} }
} }
}); });
outlineViewPanel.getExplorerManager().setRootContext( splitPane.setResizeWeight(0.5);
new TableFilterNode( splitPane.setDividerLocation(0.5);
new DataResultFilterNode(
new AbstractNode(
Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()),
true));
// When a new set of nodes are added to the OutlineView the childrenAdded
// seems to be fired before the childrenRemoved.
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override
public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
@Override
public void childrenRemoved(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
});
outlineViewPanel.setTableColumnsWidth(5,10,10,15,50,10); outlineViewPanel.setTableColumnsWidth(5,10,10,15,50,10);
} }
@Override public MessagesPanel(ChildFactory<?> nodeFactory) {
public String getDisplayName() { this();
return Bundle.MessageViewer_tabTitle(); setChildFactory(nodeFactory);
}
@Override
public JPanel getPanel() {
return this;
}
@Override
public void setSelectionInfo(SelectionInfo info) {
nodeFactory.refresh(info);
} }
@Override @Override
@ -178,13 +129,13 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
.removePropertyChangeListener("focusOwner", focusPropertyListener); .removePropertyChangeListener("focusOwner", focusPropertyListener);
} }
private void updateOutlineViewPanel() { final void setChildFactory(ChildFactory<?> nodeFactory) {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount(); outlineViewPanel.getExplorerManager().setRootContext(
if(nodeCount == 0) { new TableFilterNode(
outlineViewPanel.hideOutlineView(Bundle.MessageViewer_no_messages()); new DataResultFilterNode(
} else { new AbstractNode(
outlineViewPanel.showOutlineView(); Children.create(nodeFactory, true)),
} outlineViewPanel.getExplorerManager()),true));
} }
/** /**
@ -195,31 +146,23 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane(); splitPane = new javax.swing.JSplitPane();
contentViewer = new MessageDataContent();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messageContentViewer = new MessageDataContent();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.BorderLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setBottomComponent(contentViewer);
splitPane.setLeftComponent(outlineViewPanel); splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(messageContentViewer);
gridBagConstraints = new java.awt.GridBagConstraints(); add(splitPane, java.awt.BorderLayout.CENTER);
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(splitPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer; private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane; private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -23,12 +20,6 @@
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="org.openide.explorer.view.OutlineView" name="outlineView"> <Component class="org.openide.explorer.view.OutlineView" name="outlineView">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 400]"/>
</Property>
</Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="outlineCard"/> <CardConstraints cardName="outlineCard"/>

View File

@ -101,6 +101,15 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
outlineView.setEnabled(enabled); outlineView.setEnabled(enabled);
} }
/**
* Sets the width of the columns of the OutlineView based on the passed in
* list of percentages. There should be on double value for each column
* in the OutlineView.
*
* @param percentages A series of double percentages values representing
* what percent of the total width of the table each
* column should have.
*/
public void setTableColumnsWidth(double... percentages) { public void setTableColumnsWidth(double... percentages) {
JTable table = outlineView.getOutline(); JTable table = outlineView.getOutline();
double total = 0; double total = 0;
@ -127,11 +136,7 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
messagePanel = new javax.swing.JPanel(); messagePanel = new javax.swing.JPanel();
messageLabel = new javax.swing.JLabel(); messageLabel = new javax.swing.JLabel();
setEnabled(false);
setLayout(new java.awt.CardLayout(5, 5)); setLayout(new java.awt.CardLayout(5, 5));
outlineView.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
outlineView.setPreferredSize(new java.awt.Dimension(300, 400));
add(outlineView, "outlineCard"); add(outlineView, "outlineCard");
messagePanel.setLayout(new java.awt.BorderLayout()); messagePanel.setLayout(new java.awt.BorderLayout());

View File

@ -11,7 +11,6 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
@ -22,7 +21,7 @@
</Events> </Events>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="18" weightX="1.0" weightY="1.0"/> <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>

View File

@ -31,7 +31,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
private SelectionInfo currentSelection; private SelectionInfo currentSelection;
private final MessagesViewer messagesViewer; private final MessageViewer messagesViewer;
private final ContactsViewer contactsViewer; private final ContactsViewer contactsViewer;
private final SummaryViewer summaryViewer; private final SummaryViewer summaryViewer;
private final MediaViewer mediaViewer; private final MediaViewer mediaViewer;
@ -42,15 +42,14 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
* Creates new form RelationshipBrowser * Creates new form RelationshipBrowser
*/ */
public RelationshipBrowser() { public RelationshipBrowser() {
messagesViewer = new MessagesViewer(); initComponents();
messagesViewer = new MessageViewer();
contactsViewer = new ContactsViewer(); contactsViewer = new ContactsViewer();
summaryViewer = new SummaryViewer(); summaryViewer = new SummaryViewer();
mediaViewer = new MediaViewer(); mediaViewer = new MediaViewer();
proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup()); proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup());
initComponents();
tabPane.add(summaryViewer.getDisplayName(), summaryViewer); tabPane.add(summaryViewer.getDisplayName(), summaryViewer);
tabPane.add(messagesViewer.getDisplayName(), messagesViewer); tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
tabPane.add(contactsViewer.getDisplayName(), contactsViewer); tabPane.add(contactsViewer.getDisplayName(), contactsViewer);
@ -59,7 +58,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
/** /**
* Sets the value of currentSelection and passes the SelectionInfo onto the * Sets the value of currentSelection and passes the SelectionInfo onto the
* currently selected or visible tab. * currently selected\visible tab.
* *
* @param info Currently selected account nodes * @param info Currently selected account nodes
*/ */
@ -94,7 +93,6 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0; gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0; gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
add(tabPane, gridBagConstraints); add(tabPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents

View File

@ -0,0 +1,216 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* ChildFactory that creates createKeys and nodes from a given selectionInfo for
* only emails, call logs and messages.
*
*/
final class ThreadChildNodeFactory extends ChildFactory<BlackboardArtifact> {
private static final Logger logger = Logger.getLogger(ThreadChildNodeFactory.class.getName());
private SelectionInfo selectionInfo;
private final Action preferredAction;
/**
* Construct a new ThreadChildNodeFactory from the currently selectionInfo
*
* @param preferredAction SelectionInfo object for the currently selected
* accounts
*/
ThreadChildNodeFactory(Action preferredAction) {
this.preferredAction = preferredAction;
}
/**
* Updates the current instance of selectionInfo and calls the refresh method.
*
* @param selectionInfo New instance of the currently selected accounts
*/
public void refresh(SelectionInfo selectionInfo) {
this.selectionInfo = selectionInfo;
refresh(true);
}
/**
* Creates a list of Keys (BlackboardArtifact) for only messages for the
* currently selected accounts.
*
* @param list List of BlackboardArtifact to populate
*
* @return True on success
*/
@Override
protected boolean createKeys(List<BlackboardArtifact> list) {
CommunicationsManager communicationManager;
try {
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS
return false;
}
if(selectionInfo == null) {
return true;
}
final Set<Content> relationshipSources;
try {
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
createRootMessageKeys(list, relationshipSources) ;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
}
return true;
}
/**
* Adds only BlackboardArtifact objects to the list where are the earliest
* message in a message thread (based on threadID). If there are "unthreaded"
* messages (messages that do not have a threadID) one representitive artifact
* will be added to the list and dealt with a node creation time.
*
* @param list List to populate with BlackboardArtifact keys
* @param relationshipSources Set of Content objects
* @return True on success
* @throws TskCoreException
*/
private boolean createRootMessageKeys(List<BlackboardArtifact> list, Set<Content> relationshipSources) throws TskCoreException{
Map<String, BlackboardArtifact> rootMessageMap = new HashMap<>();
for(Content content: relationshipSources) {
if(!(content instanceof BlackboardArtifact)) {
continue;
}
BlackboardArtifact bba = (BlackboardArtifact) content;
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
// We want all artifacts that do not have "threadIDs" to appear as one thread in the UI
// To achive this assign any artifact that does not have a threadID
// the "UNTHREADED_ID"
String threadID = MessageNode.UNTHREADED_ID;
BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
if(attribute != null) {
threadID = attribute.getValueString();
}
BlackboardArtifact tableArtifact = rootMessageMap.get(threadID);
if(tableArtifact == null) {
rootMessageMap.put(threadID, bba);
} else {
// Get the date of the message
BlackboardAttribute tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT));
attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT));
// put the earliest message into the table
if(tableAttribute != null
&& attribute != null
&& tableAttribute.getValueLong() > attribute.getValueLong()) {
rootMessageMap.put(threadID, bba);
}
}
}
}
for(BlackboardArtifact bba: rootMessageMap.values()) {
list.add(bba);
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact bba) {
BlackboardAttribute attribute = null;
try {
attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to get threadID for artifact: %s", bba.getName()), ex);
}
if (attribute != null) {
return new ThreadNode(bba, attribute.getValueString(), preferredAction);
} else {
// Only one of these should occur.
return new UnthreadedNode();
}
}
/**
* An this node represents the "unthreaded" thread.
*/
final class UnthreadedNode extends AbstractNode {
/**
* Construct an instance of UnthreadNode.
*/
UnthreadedNode() {
super(Children.LEAF);
setDisplayName("Unthreaded");
this.setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/unthreaded.png" );
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
// Give this node a threadID of "UNTHEADED_ID"
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",MessageNode.UNTHREADED_ID));
return sheet;
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.communications.relationships;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* An AbstractNode subclass which wraps a MessagNode object. Doing this allows
* for the reuse of the createSheet and other function from MessageNode, but
* also some customizing of how a ThreadNode is shown.
*/
final class ThreadNode extends AbstractNode{
final private MessageNode messageNode;
ThreadNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(Children.LEAF);
messageNode = new MessageNode(artifact, threadID, preferredAction);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/threaded.png" );
}
@Override
protected Sheet createSheet() {
return messageNode.createSheet();
}
String getThreadID() {
return messageNode.getThreadID();
}
@Override
public Action getPreferredAction() {
return messageNode.getPreferredAction();
}
@Override
public String getDisplayName() {
return messageNode.getDisplayName();
}
}

View File

@ -25,7 +25,6 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
@ -78,7 +77,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.SerializationException; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.SerializationException;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtil;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator;
@ -876,13 +875,14 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
.map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList());
//Apply tags to image and write to file //Apply tags to image and write to file
BufferedImage pngImage = ImageTagsUtility.writeTags(file, regions, "png"); BufferedImage taggedImage = ImageTagsUtil.getImageWithTags(file, regions);
Path output = Paths.get(exportChooser.getSelectedFile().getPath(), Path output = Paths.get(exportChooser.getSelectedFile().getPath(),
FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS
ImageIO.write(pngImage, "png", output.toFile()); ImageIO.write(taggedImage, "png", output.toFile());
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} catch (TskCoreException | NoCurrentCaseException | IOException ex) { } catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX.
//This ensures we (devs and users) have something when it doesn't work.
LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport()); JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport());
} }

View File

@ -0,0 +1,239 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ReadContentInputStream;
/**
* Utility for drawing rectangles on image files.
*/
public final class ImageTagsUtil {
//String constant for writing PNG in ImageIO
private final static String AWT_PNG = "png";
//String constant for encoding PNG in OpenCV
private final static String OPENCV_PNG = ".png";
/**
* Creates an image with tags applied.
*
* @param file Source image.
* @param tagRegions Tags to apply.
* @return Tagged image.
*
* @throws IOException
* @throws InterruptedException Calling thread was interrupted
* @throws ExecutionException Error while reading image from AbstractFile
*/
public static BufferedImage getImageWithTags(AbstractFile file,
Collection<ImageTagRegion> tagRegions) throws IOException, InterruptedException, ExecutionException {
//The raw image in OpenCV terms
Mat sourceImage = getImageMatFromFile(file);
//Image with tags in OpenCV terms
MatOfByte taggedMatrix = getTaggedImageMatrix(sourceImage, tagRegions);
try (ByteArrayInputStream taggedStream = new ByteArrayInputStream(taggedMatrix.toArray())) {
return ImageIO.read(taggedStream);
} finally {
sourceImage.release();
taggedMatrix.release();
}
}
/**
* Get the image from file.
*
* @param file
* @return
* @throws IOException
* @throws InterruptedException
* @throws ExecutionException
*/
private static BufferedImage getImageFromFile(AbstractFile file) throws IOException, InterruptedException, ExecutionException {
if (ImageUtils.isGIF(file)) {
//Grab the first frame.
try (BufferedInputStream bufferedReadContentStream =
new BufferedInputStream(new ReadContentInputStream(file))) {
return ImageIO.read(bufferedReadContentStream);
}
} else {
//Otherwise, read the full image.
Task<Image> readImageTask = ImageUtils.newReadImageTask(file);
readImageTask.run();
Image fxResult = readImageTask.get();
return SwingFXUtils.fromFXImage(fxResult, null);
}
}
/**
* Reads the image and converts it into an OpenCV equivalent.
*
* @param file Image to read
* @return raw image bytes
*
* @throws IOException
* @throws InterruptedException Calling thread was interrupted.
* @throws ExecutionException Error while reading image from AbstractFile
*/
private static Mat getImageMatFromFile(AbstractFile file) throws InterruptedException, ExecutionException, IOException {
//Get image from file
BufferedImage buffImage = getImageFromFile(file);
//Convert it to OpenCV Mat.
try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
ImageIO.write(buffImage, AWT_PNG, outStream);
byte[] imageBytes = outStream.toByteArray();
MatOfByte rawSourceBytes = new MatOfByte(imageBytes);
Mat sourceImage = Highgui.imdecode(rawSourceBytes, Highgui.IMREAD_COLOR);
rawSourceBytes.release();
return sourceImage;
}
}
/**
* Adds tags to an image matrix.
*
* @param sourceImage
* @param tagRegions
* @param outputEncoding
* @return
*/
private static MatOfByte getTaggedImageMatrix(Mat sourceImage, Collection<ImageTagRegion> tagRegions) {
//Apply all tags to source image
for (ImageTagRegion region : tagRegions) {
Point topLeft = new Point(region.getX(), region.getY());
Point bottomRight = new Point(topLeft.x + region.getWidth(),
topLeft.y + region.getHeight());
//Red
Scalar rectangleBorderColor = new Scalar(0, 0, 255);
int rectangleBorderWidth = (int) Math.rint(region.getStrokeThickness());
Core.rectangle(sourceImage, topLeft, bottomRight,
rectangleBorderColor, rectangleBorderWidth);
}
MatOfByte taggedMatrix = new MatOfByte();
Highgui.imencode(OPENCV_PNG, sourceImage, taggedMatrix);
return taggedMatrix;
}
/**
* Creates a thumbnail with tags applied.
*
* @param file Input file to apply tags & produce thumbnail from
* @param tagRegions Tags to apply
* @param iconSize Size of the output thumbnail
* @return BufferedImage Thumbnail image
*
* @throws InterruptedException Calling thread was interrupted.
* @throws ExecutionException Error while reading image from file.
*/
public static BufferedImage getThumbnailWithTags(AbstractFile file, Collection<ImageTagRegion> tagRegions,
IconSize iconSize) throws IOException, InterruptedException, ExecutionException {
//Raw image
Mat sourceImage = getImageMatFromFile(file);
//Full size image with tags
MatOfByte taggedMatrix = getTaggedImageMatrix(sourceImage, tagRegions);
//Resized to produce thumbnail
MatOfByte thumbnailMatrix = getResizedMatrix(taggedMatrix, iconSize);
try (ByteArrayInputStream thumbnailStream = new ByteArrayInputStream(thumbnailMatrix.toArray())) {
return ImageIO.read(thumbnailStream);
} finally {
sourceImage.release();
taggedMatrix.release();
thumbnailMatrix.release();
}
}
/**
* Resizes the image matrix.
*
* @param taggedMatrix Image to resize.
* @param size Size of thumbnail.
*
* @return A new resized image matrix.
*/
private static MatOfByte getResizedMatrix(MatOfByte taggedMatrix, IconSize size) {
Size resizeDimensions = new Size(size.getSize(), size.getSize());
Mat taggedImage = Highgui.imdecode(taggedMatrix, Highgui.IMREAD_COLOR);
Mat thumbnailImage = new Mat();
Imgproc.resize(taggedImage, thumbnailImage, resizeDimensions);
MatOfByte thumbnailMatrix = new MatOfByte();
Highgui.imencode(OPENCV_PNG, thumbnailImage, thumbnailMatrix);
thumbnailImage.release();
taggedImage.release();
return thumbnailMatrix;
}
private ImageTagsUtil() {
}
/**
* Sizes for thumbnails
*/
public enum IconSize {
SMALL(50),
MEDIUM(100),
LARGE(200);
private final int SIZE;
IconSize(int size) {
this.SIZE = size;
}
public int getSize() {
return SIZE;
}
}
}

View File

@ -1,141 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utility class for handling content viewer tags on images.
*/
public final class ImageTagsUtility {
/**
* Sizes for thumbnails
*/
public enum IconSize {
SMALL(50),
MEDIUM(100),
LARGE(200);
private final int SIZE;
IconSize(int size) {
this.SIZE = size;
}
public int getSize() {
return SIZE;
}
}
/**
* Embeds the tag regions into an image.
*
* @param file Base Image
* @param tagRegions Tag regions to be saved into the image
* @param outputEncoding Format of image (jpg, png, etc). See OpenCV for
* supported formats. Do not include a "."
* @return Output image as a BufferedImage
*
* @throws TskCoreException Cannot read from abstract file
* @throws IOException Could not create buffered image from OpenCV result
*/
public static BufferedImage writeTags(AbstractFile file, Collection<ImageTagRegion> tagRegions,
String outputEncoding) throws TskCoreException, IOException {
byte[] imageInMemory = new byte[(int) file.getSize()];
file.read(imageInMemory, 0, file.getSize());
Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_UNCHANGED);
tagRegions.forEach((region) -> {
Core.rectangle(
originalImage, //Matrix obj of the image
new Point(region.getX(), region.getY()), //p1
new Point(region.getX() + region.getWidth(), region.getY() + region.getHeight()), //p2
new Scalar(0, 0, 255), //Scalar object for color
(int) Math.rint(region.getStrokeThickness())
);
});
MatOfByte matOfByte = new MatOfByte();
MatOfInt params = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 100);
Highgui.imencode("." + outputEncoding, originalImage, matOfByte, params);
try (ByteArrayInputStream imageStream = new ByteArrayInputStream(matOfByte.toArray())) {
BufferedImage result = ImageIO.read(imageStream);
originalImage.release();
matOfByte.release();
return result;
}
}
/**
* Creates a thumbnail version of the image with tags applied.
*
* @param file Input file to apply tags & produce thumbnail from
* @param tagRegions Tags to apply
* @param iconSize Size of the output thumbnail
* @param outputEncoding Format of thumbnail (jpg, png, etc). See OpenCV for
* supported formats. Do not include a "."
* @return BufferedImage representing the thumbnail
*
* @throws TskCoreException Could not read from file
* @throws IOException Could not create buffered image from OpenCV result
*/
public static BufferedImage makeThumbnail(AbstractFile file, Collection<ImageTagRegion> tagRegions,
IconSize iconSize, String outputEncoding) throws TskCoreException, IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
BufferedImage result = writeTags(file, tagRegions, outputEncoding);
ImageIO.write(result, outputEncoding, baos);
Mat markedUpImage = Highgui.imdecode(new MatOfByte(baos.toByteArray()), Highgui.IMREAD_UNCHANGED);
Mat thumbnail = new Mat();
Size resize = new Size(iconSize.getSize(), iconSize.getSize());
Imgproc.resize(markedUpImage, thumbnail, resize);
MatOfByte matOfByte = new MatOfByte();
Highgui.imencode("." + outputEncoding, thumbnail, matOfByte);
try (ByteArrayInputStream thumbnailStream = new ByteArrayInputStream(matOfByte.toArray())) {
BufferedImage thumbnailImage = ImageIO.read(thumbnailStream);
thumbnail.release();
matOfByte.release();
markedUpImage.release();
return thumbnailImage;
}
}
}
private ImageTagsUtility() {
}
}

View File

@ -302,6 +302,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
if (rootNode != null) { if (rootNode != null) {
this.rootNode = rootNode;
/** /**
* Check to see if we have previously created a paging support * Check to see if we have previously created a paging support
* class for this node. * class for this node.
@ -364,8 +366,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* case database round trips. * case database round trips.
*/ */
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
this.rootNode = rootNode;
this.getExplorerManager().setRootContext(this.rootNode); this.getExplorerManager().setRootContext(this.rootNode);
setupTable(); setupTable();
} else { } else {

View File

@ -1845,7 +1845,8 @@ final public class Accounts implements AutopsyVisitableItem {
return ICON_BASE_PATH + "WhatsApp.png"; return ICON_BASE_PATH + "WhatsApp.png";
} else { } else {
//there could be a default icon instead... //there could be a default icon instead...
throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); return ICON_BASE_PATH + "face.png";
// throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
} }
} }
} }

View File

@ -490,6 +490,12 @@ public class DataResultFilterNode extends FilterNode {
@Override @Override
public AbstractAction visit(BlackboardArtifactNode ban) { public AbstractAction visit(BlackboardArtifactNode ban) {
Action preferredAction = ban.getPreferredAction();
if(preferredAction instanceof AbstractAction) {
return (AbstractAction) preferredAction;
}
BlackboardArtifact artifact = ban.getArtifact(); BlackboardArtifact artifact = ban.getArtifact();
try { try {
if ((artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) if ((artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-2018 Basis Technology Corp. * Copyright 2014-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -286,7 +286,7 @@ public final class DataSourceIngestJob {
this.addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase); this.addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase);
this.addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase); this.addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to add ingest modules to database.", ex); logErrorMessage(Level.WARNING, "Failed to add ingest modules listing to case database", ex);
} }
} }
@ -421,13 +421,13 @@ public final class DataSourceIngestJob {
try { try {
this.ingestJob = Case.getCurrentCaseThrows().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, ""); this.ingestJob = Case.getCurrentCaseThrows().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, "");
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to add ingest job to database.", ex); logErrorMessage(Level.WARNING, "Failed to add ingest job info to case database", ex); //NON-NLS
} }
if (this.hasFirstStageDataSourceIngestPipeline() || this.hasFileIngestPipeline()) { if (this.hasFirstStageDataSourceIngestPipeline() || this.hasFileIngestPipeline()) {
logger.log(Level.INFO, "Starting first stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Starting first stage analysis"); //NON-NLS
this.startFirstStage(); this.startFirstStage();
} else if (this.hasSecondStageDataSourceIngestPipeline()) { } else if (this.hasSecondStageDataSourceIngestPipeline()) {
logger.log(Level.INFO, "Starting second stage analysis for {0} (jobId={1}), no first stage configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Starting second stage analysis"); //NON-NLS
this.startSecondStage(); this.startSecondStage();
} }
} }
@ -521,13 +521,13 @@ public final class DataSourceIngestJob {
* Schedule the first stage tasks. * Schedule the first stage tasks.
*/ */
if (this.hasFirstStageDataSourceIngestPipeline() && this.hasFileIngestPipeline()) { if (this.hasFirstStageDataSourceIngestPipeline() && this.hasFileIngestPipeline()) {
logger.log(Level.INFO, "Scheduling first stage data source and file level analysis tasks for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Scheduling first stage data source and file level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleIngestTasks(this); DataSourceIngestJob.taskScheduler.scheduleIngestTasks(this);
} else if (this.hasFirstStageDataSourceIngestPipeline()) { } else if (this.hasFirstStageDataSourceIngestPipeline()) {
logger.log(Level.INFO, "Scheduling first stage data source level analysis tasks for {0} (jobId={1}), no file level analysis configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Scheduling first stage data source level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this); DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this);
} else { } else {
logger.log(Level.INFO, "Scheduling file level analysis tasks for {0} (jobId={1}), no first stage data source level analysis configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Scheduling file level analysis tasks, no first stage data source level analysis configured"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleFileIngestTasks(this, this.files); DataSourceIngestJob.taskScheduler.scheduleFileIngestTasks(this, this.files);
/** /**
@ -546,7 +546,7 @@ public final class DataSourceIngestJob {
* Starts the second stage of this ingest job. * Starts the second stage of this ingest job.
*/ */
private void startSecondStage() { private void startSecondStage() {
logger.log(Level.INFO, "Starting second stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Starting second stage analysis"); //NON-NLS
this.stage = DataSourceIngestJob.Stages.SECOND; this.stage = DataSourceIngestJob.Stages.SECOND;
if (this.doUI) { if (this.doUI) {
this.startDataSourceIngestProgressBar(); this.startDataSourceIngestProgressBar();
@ -554,7 +554,7 @@ public final class DataSourceIngestJob {
synchronized (this.dataSourceIngestPipelineLock) { synchronized (this.dataSourceIngestPipelineLock) {
this.currentDataSourceIngestPipeline = this.secondStageDataSourceIngestPipeline; this.currentDataSourceIngestPipeline = this.secondStageDataSourceIngestPipeline;
} }
logger.log(Level.INFO, "Scheduling second stage data source level analysis tasks for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Scheduling second stage data source level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this); DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this);
} }
@ -643,7 +643,7 @@ public final class DataSourceIngestJob {
* job and starts the second stage, if appropriate. * job and starts the second stage, if appropriate.
*/ */
private void finishFirstStage() { private void finishFirstStage() {
logger.log(Level.INFO, "Finished first stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Finished first stage analysis"); //NON-NLS
// Shut down the file ingest pipelines. Note that no shut down is // Shut down the file ingest pipelines. Note that no shut down is
// required for the data source ingest pipeline because data source // required for the data source ingest pipeline because data source
@ -693,7 +693,7 @@ public final class DataSourceIngestJob {
* Shuts down the ingest pipelines and progress bars for this job. * Shuts down the ingest pipelines and progress bars for this job.
*/ */
private void finish() { private void finish() {
logger.log(Level.INFO, "Finished analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS logInfoMessage("Finished analysis"); //NON-NLS
this.stage = DataSourceIngestJob.Stages.FINALIZATION; this.stage = DataSourceIngestJob.Stages.FINALIZATION;
if (this.doUI) { if (this.doUI) {
@ -711,19 +711,19 @@ public final class DataSourceIngestJob {
try { try {
ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED); ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set ingest status for ingest job in database.", ex); logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
} }
} else { } else {
try { try {
ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED); ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set ingest status for ingest job in database.", ex); logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
} }
} }
try { try {
this.ingestJob.setEndDateTime(new Date()); this.ingestJob.setEndDateTime(new Date());
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set end date for ingest job in database.", ex); logErrorMessage(Level.WARNING, "Failed to set job end date in case database", ex);
} }
} }
this.parentJob.dataSourceJobFinished(this); this.parentJob.dataSourceJobFinished(this);
@ -842,7 +842,7 @@ public final class DataSourceIngestJob {
if (DataSourceIngestJob.Stages.FIRST == this.stage) { if (DataSourceIngestJob.Stages.FIRST == this.stage) {
DataSourceIngestJob.taskScheduler.fastTrackFileIngestTasks(this, files); DataSourceIngestJob.taskScheduler.fastTrackFileIngestTasks(this, files);
} else { } else {
DataSourceIngestJob.logger.log(Level.SEVERE, "Adding files during second stage not supported"); //NON-NLS logErrorMessage(Level.SEVERE, "Adding files to job during second stage analysis not supported");
} }
/** /**
@ -1066,6 +1066,39 @@ public final class DataSourceIngestJob {
return this.cancellationReason; return this.cancellationReason;
} }
/**
* Writes an info message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param message The message.
*/
private void logInfoMessage(String message) {
logger.log(Level.INFO, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id)); //NON-NLS
}
/**
* Writes an error message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param level The logging level for the message.
* @param message The message.
* @param throwable The throwable associated with the error.
*/
private void logErrorMessage(Level level, String message, Throwable throwable) {
logger.log(level, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id), throwable); //NON-NLS
}
/**
* Writes an error message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param level The logging level for the message.
* @param message The message.
*/
private void logErrorMessage(Level level, String message) {
logger.log(level, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id)); //NON-NLS
}
/** /**
* Write ingest module errors to the log. * Write ingest module errors to the log.
* *
@ -1073,7 +1106,7 @@ public final class DataSourceIngestJob {
*/ */
private void logIngestModuleErrors(List<IngestModuleError> errors) { private void logIngestModuleErrors(List<IngestModuleError> errors) {
for (IngestModuleError error : errors) { for (IngestModuleError error : errors) {
DataSourceIngestJob.logger.log(Level.SEVERE, String.format("%s experienced an error analyzing %s (jobId=%d)", error.getModuleDisplayName(), dataSource.getName(), this.id), error.getThrowable()); //NON-NLS logErrorMessage(Level.SEVERE, String.format("%s experienced an error during analysis", error.getModuleDisplayName()), error.getThrowable()); //NON-NLS
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2018 Basis Technology Corp. * Copyright 2012-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -403,10 +403,10 @@ public class IngestManager implements IngestProgressSnapshotProvider {
synchronized (ingestJobsById) { synchronized (ingestJobsById) {
ingestJobsById.put(job.getId(), job); ingestJobsById.put(job.getId(), job);
} }
IngestManager.logger.log(Level.INFO, "Starting ingest job {0}", job.getId()); //NON-NLS
errors = job.start(); errors = job.start();
if (errors.isEmpty()) { if (errors.isEmpty()) {
this.fireIngestJobStarted(job.getId()); this.fireIngestJobStarted(job.getId());
IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS
} else { } else {
synchronized (ingestJobsById) { synchronized (ingestJobsById) {
this.ingestJobsById.remove(job.getId()); this.ingestJobsById.remove(job.getId());

View File

@ -135,7 +135,7 @@ final class ConfigVisualPanel1 extends JPanel {
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
FileFilter filter = new FileNameExtensionFilter(Bundle.ConfigVisualPanel1_fileNameExtensionFilter(), new String[] {"json"}); // NON-NLS FileFilter filter = new FileNameExtensionFilter(Bundle.ConfigVisualPanel1_fileNameExtensionFilter(), new String[] {"json"}); // NON-NLS
fileChooser.setFileFilter(filter); fileChooser.setFileFilter(filter);
fileChooser.setSelectedFile(new File("config.json")); // default fileChooser.setSelectedFile(new File("logical-imager-config.json")); // default
fileChooser.setMultiSelectionEnabled(false); fileChooser.setMultiSelectionEnabled(false);
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String path = fileChooser.getSelectedFile().getPath(); String path = fileChooser.getSelectedFile().getPath();

View File

@ -26,15 +26,24 @@ HashDbSearchAction.noOpenCase.errMsg=No open case available.
HashDbSearchPanel.noOpenCase.errMsg=No open case available. HashDbSearchPanel.noOpenCase.errMsg=No open case available.
HashLookupSettingsPanel.centralRepo=Central Repository HashLookupSettingsPanel.centralRepo=Central Repository
HashLookupSettingsPanel.editable=Editable HashLookupSettingsPanel.editable=Editable
HashLookupSettingsPanel.indexNsrl.text=This hash set appears to be the NSRL, it will be removed from the list.\n
HashLookupSettingsPanel.indexNsrl.title=NSRL will not be indexed
HashLookupSettingsPanel.notApplicable=N/A HashLookupSettingsPanel.notApplicable=N/A
HashLookupSettingsPanel.promptMessage.deleteHashDb=This will make the hash database unavailable for lookup. Do you want to proceed?\n\nNote: The hash database can still be re-imported later. HashLookupSettingsPanel.promptMessage.deleteHashDb=This will make the hash database unavailable for lookup. Do you want to proceed?\n\nNote: The hash database can still be re-imported later.
HashLookupSettingsPanel.promptTitle.deleteHashDb=Delete Hash Database from Configuration HashLookupSettingsPanel.promptTitle.deleteHashDb=Delete Hash Database from Configuration
HashLookupSettingsPanel.readOnly=Read only HashLookupSettingsPanel.readOnly=Read only
# {0} - hash lookup name # {0} - hash lookup name
HashLookupSettingsPanel.removeDatabaseFailure.message=Failed to remove hash lookup: {0} HashLookupSettingsPanel.removeDatabaseFailure.message=Failed to remove hash lookup: {0}
# {0} - nsrlUrlAddress
HashLookupSettingsPanel.removeUnindexedNsrl.text=Instead of indexing the NSRL, please download an already indexed version available here:\n{0}
HashLookupSettingsPanel.removeUnindexedNsrl.title=Unindexed NSRL(s) will be removed
HashLookupSettingsPanel.saveFail.message=Couldn't save hash set settings. HashLookupSettingsPanel.saveFail.message=Couldn't save hash set settings.
HashLookupSettingsPanel.saveFail.title=Save Fail HashLookupSettingsPanel.saveFail.title=Save Fail
HashLookupSettingsPanel.Title=Global Hash Lookup Settings HashLookupSettingsPanel.Title=Global Hash Lookup Settings
# {0} - nsrlHashSet
HashLookupSettingsPanel.unindexedNsrl.base=The following hash set appears to be an unindexed version of the NSRL, it will be removed from the list.\nHash set:{0}\n
# {0} - nsrlHashSets
HashLookupSettingsPanel.unindexedNsrls.base=The following hash sets appear to be unindexed versions of the NSRL, they will be removed from the list.\nHash sets:{0}\n
HashLookupSettingsPanel.updateStatusError=Error reading status HashLookupSettingsPanel.updateStatusError=Error reading status
ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash set file ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash set file
ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -25,7 +25,6 @@ import java.awt.Frame;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -45,6 +44,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
@ -61,12 +61,15 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private static final String NSRL_URL = "https://sourceforge.net/projects/autopsy/files/NSRL/";
private static final String NSRL_NAME_STRING = "nsrl";
private static final String NO_SELECTION_TEXT = NbBundle private static final String NO_SELECTION_TEXT = NbBundle
.getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.noSelectionText"); .getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.noSelectionText");
private static final String ERROR_GETTING_PATH_TEXT = NbBundle private static final String ERROR_GETTING_PATH_TEXT = NbBundle
.getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.errorGettingPathText"); .getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.errorGettingPathText");
private static final String ERROR_GETTING_INDEX_STATUS_TEXT = NbBundle private static final String ERROR_GETTING_INDEX_STATUS_TEXT = NbBundle
.getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.errorGettingIndexStatusText"); .getMessage(HashLookupSettingsPanel.class, "HashDbConfigPanel.errorGettingIndexStatusText");
private static final Logger logger = Logger.getLogger(HashLookupSettingsPanel.class.getName());
private final HashDbManager hashSetManager = HashDbManager.getInstance(); private final HashDbManager hashSetManager = HashDbManager.getInstance();
private final HashSetTableModel hashSetTableModel = new HashSetTableModel(); private final HashSetTableModel hashSetTableModel = new HashSetTableModel();
private final List<Integer> newReferenceSetIDs = new ArrayList<>(); private final List<Integer> newReferenceSetIDs = new ArrayList<>();
@ -320,10 +323,8 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
@Override @Override
public void run() { public void run() {
//If unindexed ones are found, show a popup box that will either index them, or remove them. //If unindexed ones are found, show a popup box that will either index them, or remove them.
if (unindexed.size() == 1) { if (!unindexed.isEmpty()) {
showInvalidIndex(false, unindexed); showInvalidIndex(unindexed);
} else if (unindexed.size() > 1) {
showInvalidIndex(true, unindexed);
} }
} }
}); });
@ -394,16 +395,45 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
* @param plural Whether or not there are multiple unindexed databases * @param plural Whether or not there are multiple unindexed databases
* @param unindexed The list of unindexed databases. Can be of size 1. * @param unindexed The list of unindexed databases. Can be of size 1.
*/ */
private void showInvalidIndex(boolean plural, List<SleuthkitHashSet> unindexed) { @NbBundle.Messages({"# {0} - nsrlUrlAddress",
"HashLookupSettingsPanel.removeUnindexedNsrl.text=Instead of indexing the NSRL, please download an already indexed version available here:\n{0}",
"# {0} - nsrlHashSet",
"HashLookupSettingsPanel.unindexedNsrl.base=The following hash set appears to be an unindexed version of the NSRL, it will be removed from the list.\nHash set:{0}\n",
"# {0} - nsrlHashSets",
"HashLookupSettingsPanel.unindexedNsrls.base=The following hash sets appear to be unindexed versions of the NSRL, they will be removed from the list.\nHash sets:{0}\n",
"HashLookupSettingsPanel.removeUnindexedNsrl.title=Unindexed NSRL(s) will be removed"})
private void showInvalidIndex(List<SleuthkitHashSet> unindexed) {
String total = ""; String total = "";
String message; String nsrlTotal = "";
for (HashDb hdb : unindexed) {
List<SleuthkitHashSet> nsrlHashsets = new ArrayList<>();
for (SleuthkitHashSet hdb : unindexed) {
//check if this is the NSRL if so point users toward already indexed versions
if (isWindows() && hdb.getHashSetName().toLowerCase().contains(NSRL_NAME_STRING)) {
nsrlHashsets.add(hdb);
nsrlTotal += "\n" + hdb.getHashSetName();
} else {
total += "\n" + hdb.getHashSetName(); total += "\n" + hdb.getHashSetName();
} }
if (plural) { }
message = NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.dbsNotIndexedMsg", total); if (!nsrlHashsets.isEmpty()) {
String message;
if (nsrlHashsets.size() > 1) {
message = Bundle.HashLookupSettingsPanel_unindexedNsrls_base(nsrlTotal) + Bundle.HashLookupSettingsPanel_removeUnindexedNsrl_text(NSRL_URL);
} else { } else {
message = NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.dbNotIndexedMsg", total); message = Bundle.HashLookupSettingsPanel_unindexedNsrl_base(nsrlTotal) + Bundle.HashLookupSettingsPanel_removeUnindexedNsrl_text(NSRL_URL);
}
JOptionPane.showMessageDialog(this, message, Bundle.HashLookupSettingsPanel_removeUnindexedNsrl_title(), JOptionPane.INFORMATION_MESSAGE);
for (SleuthkitHashSet hdb : nsrlHashsets) {
unindexed.remove(hdb);
}
removeThese(nsrlHashsets);
}
String message = NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.dbNotIndexedMsg", total);
if (unindexed.isEmpty()) {
return;
} else if (unindexed.size() > 1) {
message = NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.dbsNotIndexedMsg", total);
} }
int res = JOptionPane.showConfirmDialog(this, message, int res = JOptionPane.showConfirmDialog(this, message,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
@ -948,6 +978,17 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
} }
}//GEN-LAST:event_sendIngestMessagesCheckBoxActionPerformed }//GEN-LAST:event_sendIngestMessagesCheckBoxActionPerformed
/**
* Check if the current OS is windows
*
* @return true if running on windows, false otherwise
*/
private boolean isWindows() {
return PlatformUtil.getOSName().toLowerCase().startsWith("Windows");
}
@NbBundle.Messages({"HashLookupSettingsPanel.indexNsrl.text=This hash set appears to be the NSRL, it will be removed from the list.\n",
"HashLookupSettingsPanel.indexNsrl.title=NSRL will not be indexed"})
private void indexButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_indexButtonActionPerformed private void indexButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_indexButtonActionPerformed
final HashDb hashDatabase = ((HashSetTable) hashSetTable).getSelection(); final HashDb hashDatabase = ((HashSetTable) hashSetTable).getSelection();
assert hashDatabase != null; assert hashDatabase != null;
@ -956,6 +997,16 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
// Add a listener for the INDEXING_DONE event. This listener will update // Add a listener for the INDEXING_DONE event. This listener will update
// the UI. // the UI.
SleuthkitHashSet hashDb = (SleuthkitHashSet) hashDatabase; SleuthkitHashSet hashDb = (SleuthkitHashSet) hashDatabase;
if (isWindows() && hashDb.getHashSetName().toLowerCase().contains(NSRL_NAME_STRING)) {
JOptionPane.showMessageDialog(this, Bundle.HashLookupSettingsPanel_indexNsrl_text() + Bundle.HashLookupSettingsPanel_removeUnindexedNsrl_text(NSRL_URL), Bundle.HashLookupSettingsPanel_indexNsrl_title(), JOptionPane.INFORMATION_MESSAGE);
try {
hashSetManager.removeHashDatabaseNoSave(hashDatabase);
hashSetTableModel.refreshModel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} catch (HashDbManager.HashDbManagerException ex) {
logger.log(Level.WARNING, "Unable to remove unindexed NSRL from hash set list", ex);
}
} else {
hashDb.addPropertyChangeListener(new PropertyChangeListener() { hashDb.addPropertyChangeListener(new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
@ -978,6 +1029,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
indexDialog.setLocationRelativeTo(null); indexDialog.setLocationRelativeTo(null);
indexDialog.setVisible(true); indexDialog.setVisible(true);
indexDialog.setModal(true); indexDialog.setModal(true);
}
}//GEN-LAST:event_indexButtonActionPerformed }//GEN-LAST:event_indexButtonActionPerformed
private void importDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importDatabaseButtonActionPerformed private void importDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importDatabaseButtonActionPerformed

View File

@ -45,6 +45,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -59,7 +60,7 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtil;
import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -805,10 +806,10 @@ class ReportHTML implements TableReportModule {
if(!imageTags.isEmpty()) { if(!imageTags.isEmpty()) {
//Write the tags to the fullsize and thumbnail images //Write the tags to the fullsize and thumbnail images
BufferedImage fullImageWithTags = ImageTagsUtility.writeTags(file, imageTags, "png"); BufferedImage fullImageWithTags = ImageTagsUtil.getImageWithTags(file, imageTags);
BufferedImage thumbnailImageWithTags = ImageTagsUtility.makeThumbnail(file, BufferedImage thumbnailWithTags = ImageTagsUtil.getThumbnailWithTags(file,
imageTags, ImageTagsUtility.IconSize.MEDIUM, "png"); imageTags, ImageTagsUtil.IconSize.MEDIUM);
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName()); String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
@ -819,7 +820,7 @@ class ReportHTML implements TableReportModule {
File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile(); File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
//Save images //Save images
ImageIO.write(thumbnailImageWithTags, "png", thumbnailImageWithTagsFile); ImageIO.write(thumbnailWithTags, "png", thumbnailImageWithTagsFile);
ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile); ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile);
thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName(); thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
@ -828,7 +829,7 @@ class ReportHTML implements TableReportModule {
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS
} catch (IOException ex) { } catch (IOException | InterruptedException | ExecutionException ex) {
logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2014-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -91,6 +91,7 @@ import org.sleuthkit.datamodel.TskCoreException;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class TimeLineTopComponent extends TopComponent implements ExplorerManager.Provider { public final class TimeLineTopComponent extends TopComponent implements ExplorerManager.Provider {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(TimeLineTopComponent.class.getName()); private static final Logger logger = Logger.getLogger(TimeLineTopComponent.class.getName());
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@ -102,7 +103,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private final ExplorerManager explorerManager = new ExplorerManager(); private final ExplorerManager explorerManager = new ExplorerManager();
private final TimeLineController controller; private TimeLineController controller;
/** /**
* Lookup that will be exposed through the (Global Actions Context) * Lookup that will be exposed through the (Global Actions Context)
@ -253,11 +254,12 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
} }
/** /**
* Constructor * Constructs a "shell" version of the top component for the Timeline
* * feature which has only Swing components, no controller, and no listeners.
* @param controller The TimeLineController for this topcomponent. * This constructor conforms to the NetBeans window system requirement that
* all top components have a public, no argument constructor.
*/ */
public TimeLineTopComponent(TimeLineController controller) { public TimeLineTopComponent() {
initComponents(); initComponents();
associateLookup(proxyLookup); associateLookup(proxyLookup);
setName(NbBundle.getMessage(TimeLineTopComponent.class, "CTL_TimeLineTopComponent")); setName(NbBundle.getMessage(TimeLineTopComponent.class, "CTL_TimeLineTopComponent"));
@ -266,7 +268,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
getActionMap().put("addBookmarkTag", new AddBookmarkTagAction()); //NON-NLS getActionMap().put("addBookmarkTag", new AddBookmarkTagAction()); //NON-NLS
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ExternalViewerShortcutAction.EXTERNAL_VIEWER_SHORTCUT, "useExternalViewer"); //NON-NLS getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ExternalViewerShortcutAction.EXTERNAL_VIEWER_SHORTCUT, "useExternalViewer"); //NON-NLS
getActionMap().put("useExternalViewer", ExternalViewerShortcutAction.getInstance()); //NON-NLS getActionMap().put("useExternalViewer", ExternalViewerShortcutAction.getInstance()); //NON-NLS
this.controller = controller;
//create linked result and content views //create linked result and content views
contentViewerPanel = new DataContentExplorerPanel(); contentViewerPanel = new DataContentExplorerPanel();
@ -278,6 +279,17 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
dataResultPanel.open(); //get the explorermanager dataResultPanel.open(); //get the explorermanager
contentViewerPanel.initialize(); contentViewerPanel.initialize();
}
/**
* Constructs a fully functional top component for the Timeline feature.
*
* @param controller The TimeLineController for this top component.
*/
public TimeLineTopComponent(TimeLineController controller) {
this();
this.controller = controller;
Platform.runLater(this::initFXComponents); Platform.runLater(this::initFXComponents);

View File

@ -41,6 +41,9 @@ public final class OpenCvLoader {
* Return whether or not the OpenCV library has been loaded. * Return whether or not the OpenCV library has been loaded.
* *
* @return - true if the opencv library is loaded or false if it is not * @return - true if the opencv library is loaded or false if it is not
* @throws UnsatisfiedLinkError - A COPY of the exception that prevented OpenCV from loading.
* Note that the stack trace in the exception can be confusing because it refers to a
* past invocation.
*/ */
public static boolean isOpenCvLoaded() throws UnsatisfiedLinkError { public static boolean isOpenCvLoaded() throws UnsatisfiedLinkError {
if (!OPEN_CV_LOADED) { if (!OPEN_CV_LOADED) {

View File

@ -99,13 +99,14 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
resultSet = None resultSet = None
try: try:
resultSet = statement.executeQuery( resultSet = statement.executeQuery(
"SELECT address, date, read, type, subject, body FROM sms;") "SELECT address, date, read, type, subject, body, thread_id FROM sms;")
while resultSet.next(): while resultSet.next():
address = resultSet.getString("address") # may be phone number, or other addresses address = resultSet.getString("address") # may be phone number, or other addresses
date = Long.valueOf(resultSet.getString("date")) / 1000 date = Long.valueOf(resultSet.getString("date")) / 1000
read = resultSet.getInt("read") # may be unread = 0, read = 1 read = resultSet.getInt("read") # may be unread = 0, read = 1
subject = resultSet.getString("subject") # message subject subject = resultSet.getString("subject") # message subject
body = resultSet.getString("body") # message body body = resultSet.getString("body") # message body
thread_id = "{0}_{1}".format(abstractFile.getId(), resultSet.getInt("thread_id"))
attributes = ArrayList() attributes = ArrayList()
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set. artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set.
if resultSet.getString("type") == "1": if resultSet.getString("type") == "1":
@ -119,6 +120,7 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message")) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message"))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, thread_id))
artifact.addAttributes(attributes) artifact.addAttributes(attributes)

View File

@ -41,6 +41,8 @@ from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel import TskCoreException
from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel import Account
from org.sleuthkit.datamodel import Relationship from org.sleuthkit.datamodel import Relationship
from org.sleuthkit.autopsy.ingest import IngestServices
from org.sleuthkit.autopsy.ingest import ModuleDataEvent
import traceback import traceback
import general import general
@ -79,6 +81,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
if not databasePath: if not databasePath:
return return
bbartifacts = list()
try: try:
Class.forName("org.sqlite.JDBC"); # load JDBC driver Class.forName("org.sqlite.JDBC"); # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
@ -115,6 +118,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message"))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, user_id))
artifact.addAttributes(attributes) artifact.addAttributes(attributes)
@ -124,6 +128,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
# create relationship between accounts # create relationship between accounts
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at); Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at);
bbartifacts.append(artifact)
try: try:
# index the artifact for keyword search # index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard() blackboard = Case.getCurrentCase().getServices().getBlackboard()
@ -140,6 +145,10 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex) self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())
finally: finally:
if bbartifacts:
IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE, bbartifacts))
try: try:
if resultSet is not None: if resultSet is not None:
resultSet.close() resultSet.close()

View File

@ -1,3 +1,3 @@
<project name="TSK_VERSION"> <project name="TSK_VERSION">
<property name="TSK_VERSION" value="4.6.6"/> <property name="TSK_VERSION" value="4.6.7"/>
</project> </project>

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 4.11.0 PROJECT_NUMBER = 4.12.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
@ -1025,7 +1025,7 @@ GENERATE_HTML = YES
# The default directory is: html. # The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = 4.11.0 HTML_OUTPUT = 4.12.0
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp). # generated HTML page (for example: .htm, .php, .asp).

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 4.11.0 PROJECT_NUMBER = 4.12.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears a the top of each page and should give viewer a # for a project that appears a the top of each page and should give viewer a
@ -1063,7 +1063,7 @@ GENERATE_HTML = YES
# The default directory is: html. # The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = api-docs/4.11.0/ HTML_OUTPUT = api-docs/4.12.0/
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp). # generated HTML page (for example: .htm, .php, .asp).

View File

@ -4,10 +4,10 @@ app.title=Autopsy
### lowercase version of above ### lowercase version of above
app.name=${branding.token} app.name=${branding.token}
### if left unset, version will default to today's date ### if left unset, version will default to today's date
app.version=4.11.0 app.version=4.12.0
### build.type must be one of: DEVELOPMENT, RELEASE ### build.type must be one of: DEVELOPMENT, RELEASE
#build.type=RELEASE build.type=RELEASE
build.type=DEVELOPMENT #build.type=DEVELOPMENT
project.org.netbeans.progress=org-netbeans-api-progress project.org.netbeans.progress=org-netbeans-api-progress
project.org.sleuthkit.autopsy.experimental=Experimental project.org.sleuthkit.autopsy.experimental=Experimental

View File

@ -5,7 +5,7 @@
# NOTE: update_sleuthkit_version.pl updates this value and relies # NOTE: update_sleuthkit_version.pl updates this value and relies
# on it keeping the same name and whitespace. Don't change it. # on it keeping the same name and whitespace. Don't change it.
TSK_VERSION=4.6.6 TSK_VERSION=4.6.7
# In the beginning... # In the beginning...