Merge remote-tracking branch 'upstream/develop' into improve-open-multi-user-case-dialog-performance

This commit is contained in:
Richard Cordovano 2019-01-30 10:15:28 -05:00
commit 690aab95c3
17 changed files with 1175 additions and 90 deletions

View File

@ -77,6 +77,7 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributeSearchAction;
@ -310,6 +311,11 @@ public class Case {
* was deleted (type: Long), the new value is null.
*/
DATA_SOURCE_DELETED,
/**
* A data source's name has changed. The new value of the property
* change event is the new name.
*/
DATA_SOURCE_NAME_CHANGED,
/**
* The current case has changed.
*
@ -1584,6 +1590,19 @@ public class Case {
eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
}
/**
* Notifies case event subscribers that a data source has been added to the
* case database.
*
* This should not be called from the event dispatch thread (EDT)
*
* @param dataSource The data source.
* @param newName The new name for the data source
*/
public void notifyDataSourceNameChanged(Content dataSource, String newName) {
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
}
/**
* Notifies case event subscribers that a content tag has been added.
*

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015-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.casemodule.events;
import java.io.Serializable;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*/
public class DataSourceNameChangedEvent extends AutopsyEvent implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DataSourceAddedEvent.class.getName());
private transient Content dataSource;
private final String newName;
/**
* Constructs an event published when a data source is added to a case.
*
* @param dataSource The data source that was added.
* @param newName The new name of the data source
*/
public DataSourceNameChangedEvent(Content dataSource, String newName) {
/**
* Putting the object id of the data source into newValue to allow for
* lazy loading of the Content object. This bypasses the issues related
* to the serialization and de-serialization of Content objects when the
* event is published over a network.
*/
super(Case.Events.DATA_SOURCE_NAME_CHANGED.toString(), null, dataSource.getId());
this.dataSource = dataSource;
this.newName = newName;
}
/**
* Gets the new name for the data source
*
* @return The new name for the data source
*/
@Override
public Object getNewValue() {
return newName;
}
/**
* Gets the data source that was added.
*
* @return The data source.
*/
public Content getDataSource() {
/**
* The dataSource field is set in the constructor, but it is transient
* so it will become null when the event is serialized for publication
* over a network. Doing a lazy load of the Content object bypasses the
* issues related to the serialization and de-serialization of Content
* objects and may also save database round trips from other nodes since
* subscribers to this event are often not interested in the event data.
*/
if (null != dataSource) {
return dataSource;
}
try {
long id = (Long) super.getNewValue();
dataSource = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(id);
return dataSource;
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS
return null;
}
}
}

View File

@ -900,6 +900,50 @@ abstract class AbstractSqlEamDb implements EamDb {
}
}
/**
* Changes the name of a data source in the DB
*
* @param eamDataSource The data source
* @param newName The new name
*
* @throws EamDbException
*/
@Override
public void updateDataSourceName(CorrelationDataSource eamDataSource, String newName) throws EamDbException {
Connection conn = connect();
PreparedStatement preparedStatement = null;
String sql = "UPDATE data_sources SET name = ? WHERE id = ?";
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, newName);
preparedStatement.setInt(2, eamDataSource.getID());
preparedStatement.executeUpdate();
CorrelationDataSource updatedDataSource = new CorrelationDataSource(
eamDataSource.getCaseID(),
eamDataSource.getID(),
eamDataSource.getDeviceID(),
newName,
eamDataSource.getDataSourceObjectID(),
eamDataSource.getMd5(),
eamDataSource.getSha1(),
eamDataSource.getSha256());
dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(updatedDataSource.getCaseID(), updatedDataSource.getDataSourceObjectID()), updatedDataSource);
dataSourceCacheById.put(getDataSourceByIdCacheKey(updatedDataSource.getCaseID(), updatedDataSource.getID()), updatedDataSource);
} catch (SQLException ex) {
throw new EamDbException("Error updating name of data source with ID " + eamDataSource.getDataSourceObjectID()
+ " to " + newName, ex); // NON-NLS
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeConnection(conn);
}
}
/**
* Inserts new Artifact(s) into the database. Should add associated Case and
* Data Source first.

View File

@ -133,6 +133,9 @@ public class EamArtifactUtil {
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID);
}
}
} catch (EamDbException ex) {

View File

@ -255,6 +255,16 @@ public interface EamDb {
*/
List<CorrelationDataSource> getDataSources() throws EamDbException;
/**
* Changes the name of a data source in the DB
*
* @param eamDataSource The data source
* @param newName The new name
*
* @throws EamDbException
*/
void updateDataSourceName(CorrelationDataSource eamDataSource, String newName) throws EamDbException;
/**
* Inserts new Artifact(s) into the database. Should add associated Case and
* Data Source first.

View File

@ -434,6 +434,24 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
}
/**
* Changes the name of a data source in the DB
*
* @param eamDataSource The data source
* @param newName The new name
*
* @throws EamDbException
*/
@Override
public void updateDataSourceName(CorrelationDataSource eamDataSource, String newName) throws EamDbException {
try {
acquireExclusiveLock();
super.updateDataSourceName(eamDataSource, newName);
} finally {
releaseExclusiveLock();
}
}
/**
* Updates the MD5 hash value in an existing data source in the database.
*

View File

@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -34,6 +35,7 @@ import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -106,6 +108,10 @@ final class CaseEventListener implements PropertyChangeListener {
jobProcessingExecutor.submit(new CurrentCaseTask(dbManager, evt));
}
break;
case DATA_SOURCE_NAME_CHANGED: {
jobProcessingExecutor.submit(new DataSourceNameChangedTask(dbManager, evt));
}
break;
}
}
@ -489,4 +495,40 @@ final class CaseEventListener implements PropertyChangeListener {
}
} // CURRENT_CASE
}
private final class DataSourceNameChangedTask implements Runnable {
private final EamDb dbManager;
private final PropertyChangeEvent event;
private DataSourceNameChangedTask(EamDb db, PropertyChangeEvent evt) {
dbManager = db;
event = evt;
}
@Override
public void run() {
final DataSourceNameChangedEvent dataSourceNameChangedEvent = (DataSourceNameChangedEvent) event;
Content dataSource = dataSourceNameChangedEvent.getDataSource();
String newName = (String) event.getNewValue();
if (! StringUtils.isEmpty(newName)) {
if (!EamDb.isEnabled()) {
return;
}
try {
CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCaseThrows());
CorrelationDataSource existingEamDataSource = dbManager.getDataSource(correlationCase, dataSource.getId());
dbManager.updateDataSourceName(existingEamDataSource, newName);
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error updating data source with ID " + dataSource.getId() + " to " + newName, ex); //NON-NLS
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "No open case", ex);
}
}
} // DATA_SOURCE_NAME_CHANGED
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014-2018 Basis Technology Corp.
* Copyright 2014-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -27,7 +27,6 @@ import java.util.prefs.Preferences;
import org.openide.util.NbPreferences;
import org.python.icu.util.TimeZone;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.TextConverterException;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
@ -76,6 +75,7 @@ public final class UserPreferences {
public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences";
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
// Prevent instantiation.
private UserPreferences() {
@ -473,6 +473,24 @@ public final class UserPreferences {
preferences.putInt(MAX_NUM_OF_LOG_FILE, count);
}
/**
* Get the maximum JVM heap size (in MB) for the embedded Solr server.
*
* @return Saved value or default (512)
*/
public static int getMaxSolrVMSize() {
return preferences.getInt(SOLR_MAX_JVM_SIZE, 512);
}
/**
* Set the maximum JVM heap size (in MB) for the embedded Solr server.
*
* @param maxSize
*/
public static void setMaxSolrVMSize(int maxSize) {
preferences.putInt(SOLR_MAX_JVM_SIZE, maxSize);
}
/**
* Set the HdX path.
*

View File

@ -78,7 +78,7 @@
</folder>
<folder name="Tools">
<file name="org-sleuthkit-autopsy-filesearch-FileSearchAction.instance"/>
<file name="org-sleuthkit-autopsy-commonfilesearch-CommonAttributeSearchAction.instance"/>
<file name="org-sleuthkit-autopsy-commonpropertiessearch-CommonAttributeSearchAction.instance"/>
<file name="org-sleuthkit-autopsy-ingest-IngestMessagesAction.instance">
<attr name="delegate" newvalue="org.sleuthkit.autopsy.ingest.IngestMessagesAction"/>
</file>
@ -202,8 +202,8 @@
<file name="org-netbeans-modules-options-OptionsWindowAction.shadow"/>
<file name="org-netbeans-modules-templates-actions-TemplatesAction.shadow_hidden"/>
<file name="org-openide-actions-ToolsAction.shadow_hidden"/>
<file name="org-sleuthkit-autopsy-commonfilesearch-CommonFilesAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-commonfilesearch-CommonAttributeSearchAction.instance"/>
<file name="org-sleuthkit-autopsy-commonpropertiessearch-CommonFilesAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-commonpropertiessearch-CommonAttributeSearchAction.instance"/>
<attr name="position" intvalue="202"/>
</file>
<file name="org-sleuthkit-autopsy-filesearch-FileSearchAction.shadow">

View File

@ -68,7 +68,7 @@
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="runtimePanel" max="32767" attributes="0"/>
<Component id="logoPanel" alignment="0" pref="1002" max="32767" attributes="0"/>
<Component id="logoPanel" alignment="0" pref="1010" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -81,7 +81,7 @@
<Component id="runtimePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="logoPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="185" max="32767" attributes="0"/>
<EmptySpace pref="191" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -121,7 +121,7 @@
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="agencyLogoPreview" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="479" max="32767" attributes="0"/>
<EmptySpace pref="456" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -242,37 +242,35 @@
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="totalMemoryLabel" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="systemMemoryTotal" min="-2" pref="37" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="maxMemoryUnitsLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="restartNecessaryWarning" pref="783" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="maxMemoryUnitsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="memFieldValidationLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<Component id="totalMemoryLabel" linkSize="1" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="maxSolrMemoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="maxMemoryLabel" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="memField" linkSize="3" min="-2" pref="37" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="maxLogFileCount" linkSize="1" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="12" pref="12" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="logFileCount" linkSize="3" alignment="1" min="-2" pref="37" max="-2" attributes="0"/>
<Component id="solrMaxHeapSpinner" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="memField" linkSize="3" alignment="1" min="-2" pref="37" max="-2" attributes="0"/>
<Component id="systemMemoryTotal" alignment="1" min="-2" pref="37" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="maxMemoryUnitsLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="maxMemoryUnitsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="maxMemoryUnitsLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="120" max="-2" attributes="0"/>
<Component id="logNumAlert" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="maxLogFileCount" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="logFileCount" linkSize="3" min="-2" pref="37" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="logNumAlert" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="restartNecessaryWarning" alignment="0" pref="718" max="32767" attributes="0"/>
<Component id="memFieldValidationLabel" alignment="0" min="-2" pref="478" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
@ -292,21 +290,27 @@
<Component id="systemMemoryTotal" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="maxMemoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="memField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="maxMemoryUnitsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="memFieldValidationLabel" alignment="0" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="logNumAlert" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="3" attributes="0">
<Component id="maxSolrMemoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="maxMemoryUnitsLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="solrMaxHeapSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="memFieldValidationLabel" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="maxLogFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="logFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="logNumAlert" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="14" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -404,6 +408,25 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="maxSolrMemoryLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.maxSolrMemoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="maxMemoryUnitsLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.maxMemoryUnitsLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="solrMaxHeapSpinner">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="solrMaxHeapSpinnerStateChanged"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -75,7 +75,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final String ETC_FOLDER_NAME = "etc";
private static final String CONFIG_FILE_EXTENSION = ".conf";
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
private static final long MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes
private static final int MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes
private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
@ -96,8 +96,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
//Is the safest course of action
//And the file won't exist in the install folder when running through netbeans
memField.setEnabled(false);
solrMaxHeapSpinner.setEnabled(false);
}
systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB()));
// The cast to int in the following is to ensure that the correct SpinnerNumberModel
// constructor is called.
solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
512, ((int)getSystemMemoryInGB()) * MEGA_IN_GIGA, 512));
textFieldListener = new TextFieldListener();
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
@ -292,6 +297,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
agencyLogoPathField.setEnabled(!useDefault);
browseLogosButton.setEnabled(!useDefault);
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
solrMaxHeapSpinner.setValue(UserPreferences.getMaxSolrVMSize());
try {
updateAgencyLogo(path);
} catch (IOException ex) {
@ -349,6 +355,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
} else {
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, "");
}
UserPreferences.setMaxSolrVMSize((int)solrMaxHeapSpinner.getValue());
if (memField.isEnabled()) { //if the field could of been changed we need to try and save it
try {
writeEtcConfFile();
@ -531,6 +538,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
maxLogFileCount = new javax.swing.JLabel();
logFileCount = new javax.swing.JTextField();
logNumAlert = new javax.swing.JTextField();
maxSolrMemoryLabel = new javax.swing.JLabel();
maxMemoryUnitsLabel2 = new javax.swing.JLabel();
solrMaxHeapSpinner = new javax.swing.JSpinner();
setPreferredSize(new java.awt.Dimension(1022, 488));
@ -595,7 +605,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addComponent(agencyLogoPathFieldValidationLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(agencyLogoPreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(479, Short.MAX_VALUE))
.addContainerGap(456, Short.MAX_VALUE))
);
logoPanelLayout.setVerticalGroup(
logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -653,6 +663,16 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
logNumAlert.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.logNumAlert.text")); // NOI18N
logNumAlert.setBorder(null);
org.openide.awt.Mnemonics.setLocalizedText(maxSolrMemoryLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxSolrMemoryLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(maxMemoryUnitsLabel2, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryUnitsLabel2.text")); // NOI18N
solrMaxHeapSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
solrMaxHeapSpinnerStateChanged(evt);
}
});
javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel);
runtimePanel.setLayout(runtimePanelLayout);
runtimePanelLayout.setHorizontalGroup(
@ -660,32 +680,30 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(runtimePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(totalMemoryLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(systemMemoryTotal, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(6, 6, 6)
.addComponent(maxSolrMemoryLabel)
.addComponent(maxMemoryLabel)
.addComponent(maxLogFileCount))
.addGap(12, 12, 12)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(logFileCount, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(solrMaxHeapSpinner, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(memField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(systemMemoryTotal, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(maxMemoryUnitsLabel1)
.addComponent(maxMemoryUnitsLabel)
.addComponent(maxMemoryUnitsLabel2))
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(maxMemoryUnitsLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.DEFAULT_SIZE, 783, Short.MAX_VALUE))
.addGap(120, 120, 120)
.addComponent(logNumAlert))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(maxMemoryUnitsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(memFieldValidationLabel)
.addGap(0, 0, Short.MAX_VALUE))))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(maxMemoryLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(memField, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(maxLogFileCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(logNumAlert)))
.addGap(23, 23, 23)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.DEFAULT_SIZE, 718, Short.MAX_VALUE)
.addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap())
);
@ -704,18 +722,23 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addComponent(maxMemoryUnitsLabel1))
.addComponent(systemMemoryTotal, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(maxMemoryLabel)
.addComponent(memField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(maxMemoryUnitsLabel))
.addComponent(memFieldValidationLabel))
.addComponent(maxMemoryUnitsLabel)
.addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(logNumAlert, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(maxSolrMemoryLabel)
.addComponent(maxMemoryUnitsLabel2)
.addComponent(solrMaxHeapSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(maxLogFileCount)
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(logNumAlert, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(14, Short.MAX_VALUE))
);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
@ -726,7 +749,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(runtimePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1002, Short.MAX_VALUE))
.addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE))
.addContainerGap())
);
jPanel1Layout.setVerticalGroup(
@ -736,7 +759,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addComponent(runtimePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(logoPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(185, Short.MAX_VALUE))
.addContainerGap(191, Short.MAX_VALUE))
);
jScrollPane1.setViewportView(jPanel1);
@ -824,6 +847,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_browseLogosButtonActionPerformed
private void solrMaxHeapSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_solrMaxHeapSpinnerStateChanged
int value = (int)solrMaxHeapSpinner.getValue();
if (value == UserPreferences.getMaxSolrVMSize()) {
// if the value hasn't changed there's nothing to do.
return;
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_solrMaxHeapSpinnerStateChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField agencyLogoPathField;
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
@ -842,10 +874,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JLabel maxMemoryLabel;
private javax.swing.JLabel maxMemoryUnitsLabel;
private javax.swing.JLabel maxMemoryUnitsLabel1;
private javax.swing.JLabel maxMemoryUnitsLabel2;
private javax.swing.JLabel maxSolrMemoryLabel;
private javax.swing.JTextField memField;
private javax.swing.JLabel memFieldValidationLabel;
private javax.swing.JLabel restartNecessaryWarning;
private javax.swing.JPanel runtimePanel;
private javax.swing.JSpinner solrMaxHeapSpinner;
private javax.swing.JRadioButton specifyLogoRB;
private javax.swing.JLabel systemMemoryTotal;
private javax.swing.JLabel totalMemoryLabel;

View File

@ -155,7 +155,7 @@ AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory:
AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:
AutopsyOptionsPanel.maxLogFileCount.text=Maximum Log Files:
AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB
AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any changes to max memory to take effect.
AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any memory changes to take effect.
AutopsyOptionsPanel.browseLogosButton.text=Browse
AutopsyOptionsPanel.defaultLogoRB.text=Use default
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
@ -224,3 +224,5 @@ ExternalViewerGlobalSettingsPanel.deleteRuleButton.text_1=Delete Rule
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text_1=Set aplication viewer to use for files with specific mime types/extensions:
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title1_1=Application
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extension
AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory:
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB

View File

@ -207,6 +207,7 @@ class AddArchiveTask implements Runnable {
DataSource ds = (DataSource) c;
String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName();
ds.setDisplayName(newName);
currentCase.notifyDataSourceNameChanged(c, newName);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -182,7 +182,6 @@ public class Server {
private String javaPath = "java";
public static final Charset DEFAULT_INDEXED_TEXT_CHARSET = Charset.forName("UTF-8"); ///< default Charset to index text as
private Process curSolrProcess = null;
private static final int MAX_SOLR_MEM_MB = 512; //TODO set dynamically based on avail. system resources
static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
static final String PROPERTIES_CURRENT_SERVER_PORT = "IndexingServerPort"; //NON-NLS
static final String PROPERTIES_CURRENT_STOP_PORT = "IndexingServerStopPort"; //NON-NLS
@ -362,7 +361,8 @@ public class Server {
* @throws IOException
*/
private Process runSolrCommand(List<String> solrArguments) throws IOException {
final String MAX_SOLR_MEM_MB_PAR = "-Xmx" + Integer.toString(MAX_SOLR_MEM_MB) + "m"; //NON-NLS
final String MAX_SOLR_MEM_MB_PAR = "-Xmx" + UserPreferences.getMaxSolrVMSize() + "m"; //NON-NLS
List<String> commandLine = new ArrayList<>();
commandLine.add(javaPath);
commandLine.add(MAX_SOLR_MEM_MB_PAR);

View File

@ -22,6 +22,8 @@ Chrome.getDownload.errMsg.errGettingFiles=Error when trying to get Chrome histor
Chrome.getDownload.errMsg.errAnalyzeFiles1={0}\: Error while trying to analyze file\:{1}
Chrome.getLogin.errMsg.errGettingFiles=Error when trying to get Chrome history files.
Chrome.getLogin.errMsg.errAnalyzingFiles={0}\: Error while trying to analyze file\:{1}
Chrome.getAutofill.errMsg.errGettingFiles=Error when trying to get Chrome Web Data files.
Chrome.getAutofill.errMsg.errAnalyzingFiles={0}\: Error while trying to analyze file\:{1}
Extract.dbConn.errMsg.failedToQueryDb={0}\: Failed to query database.
ExtractIE.moduleName.text=Internet Explorer
ExtractIE.getBookmark.errMsg.errGettingBookmarks={0}\: Error getting Internet Explorer Bookmarks.
@ -54,6 +56,12 @@ Firefox.moduleName=FireFox
Firefox.getHistory.errMsg.errFetchingFiles=Error fetching internet history files for Firefox.
Firefox.getHistory.errMsg.noFilesFound=No FireFox history files found.
Firefox.getHistory.errMsg.errAnalyzeFile={0}\: Error while trying to analyze file\:{1}
Firefox.getFormsAutofill.errMsg.errFetchingFiles=Error fetching form history file for Firefox.
Firefox.getFormsAutofill.errMsg.noFilesFound=No FireFox form history files found.
Firefox.getFormsAutofill.errMsg.errAnalyzeFile={0}\: Error while trying to analyze file\:{1}
Firefox.getAutofillProfiles.errMsg.errFetchingFiles=Error fetching Autofill Profiles file for Firefox.
Firefox.getAutofillProfiles.errMsg.noFilesFound=No FireFox Autofill Profiles files found.
Firefox.getAutofillProfiles.errMsg.errAnalyzeFile={0}\: Error while trying to analyze file\:{1}
Firefox.parentModuleName.noSpace=RecentActivity
Firefox.parentModuleName=Recent Activity
Firefox.getBookmark.errMsg.errFetchFiles=Error fetching bookmark files for Firefox.

View File

@ -37,12 +37,15 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -62,7 +65,19 @@ class Chrome extends Extract {
private static final String COOKIE_QUERY = "SELECT name, value, host_key, expires_utc,last_access_utc, creation_utc FROM cookies"; //NON-NLS
private static final String DOWNLOAD_QUERY = "SELECT full_path, url, start_time, received_bytes FROM downloads"; //NON-NLS
private static final String DOWNLOAD_QUERY_V30 = "SELECT current_path AS full_path, url, start_time, received_bytes FROM downloads, downloads_url_chains WHERE downloads.id=downloads_url_chains.id"; //NON-NLS
private static final String LOGIN_QUERY = "SELECT origin_url, username_value, signon_realm from logins"; //NON-NLS
private static final String LOGIN_QUERY = "SELECT origin_url, username_value, date_created, signon_realm from logins"; //NON-NLS
private static final String AUTOFILL_QUERY = "SELECT name, value, count, date_created " +
" FROM autofill, autofill_dates " +
" WHERE autofill.pair_id = autofill_dates.pair_id"
; //NON-NLS
private static final String AUTOFILL_QUERY_V8X = "SELECT name, value, count, date_created, date_last_used from autofill"; //NON-NLS
private static final String WEBFORM_ADDRESS_QUERY = "SELECT first_name, middle_name, last_name, address_line_1, address_line_2, city, state, zipcode, country_code, number, email, date_modified " +
" FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
" WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
private static final String WEBFORM_ADDRESS_QUERY_V8X = "SELECT first_name, middle_name, last_name, full_name, street_address, city, state, zipcode, country_code, number, email, date_modified, use_date, use_count" +
" FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
" WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
private final Logger logger = Logger.getLogger(this.getClass().getName());
private Content dataSource;
private IngestJobContext context;
@ -79,6 +94,8 @@ class Chrome extends Extract {
this.getHistory();
this.getBookmark();
this.getCookie();
this.getLogins();
this.getAutofill();
this.getDownload();
}
@ -517,6 +534,348 @@ class Chrome extends Extract {
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
}
/**
* Gets user logins from Login Data sqlite database
*/
private void getLogins() {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> loginDataFiles;
try {
loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
this.addErrorMessage(this.getName() + ": " + msg);
return;
}
if (loginDataFiles.isEmpty()) {
logger.log(Level.INFO, "Didn't find any Chrome Login Data files."); //NON-NLS
return;
}
dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0;
while (j < loginDataFiles.size()) {
AbstractFile loginDataFile = loginDataFiles.get(j++);
if (loginDataFile.getSize() == 0) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading Chrome login artifacts file '%s' (id=%d).",
loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
this.getName(), loginDataFile.getName()));
continue;
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome login artifacts file '%s' (id=%d).",
temps, loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
this.getName(), loginDataFile.getName()));
continue;
}
File dbFile = new File(temps);
if (context.dataSourceIngestIsCancelled()) {
dbFile.delete();
break;
}
List<HashMap<String, Object>> tempList = this.dbConnect(temps, LOGIN_QUERY);
logger.log(Level.INFO, "{0}- Now getting login information from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
(Long.valueOf(result.get("date_created").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
(NetworkUtils.extractDomain((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : "")))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
if (bbart != null) {
this.indexArtifact(bbart);
bbartifacts.add(bbart);
}
}
dbFile.delete();
}
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
BlackboardArtifact.ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, bbartifacts));
}
/**
* Gets and parses Autofill data from 'Web Data' database,
* and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts
*/
private void getAutofill() {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> webDataFiles;
try {
webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
this.addErrorMessage(this.getName() + ": " + msg);
return;
}
if (webDataFiles.isEmpty()) {
logger.log(Level.INFO, "Didn't find any Chrome Web Data files."); //NON-NLS
return;
}
dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0;
while (j < webDataFiles.size()) {
AbstractFile webDataFile = webDataFiles.get(j++);
if (webDataFile.getSize() == 0) {
continue;
}
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading Chrome Autofill artifacts file '%s' (id=%d).",
webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getAutofill.errMsg.errAnalyzingFiles",
this.getName(), webDataFile.getName()));
continue;
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome Web data file '%s' (id=%d).",
tempFilePath, webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
this.getName(), webDataFile.getName()));
continue;
}
File dbFile = new File(tempFilePath);
if (context.dataSourceIngestIsCancelled()) {
dbFile.delete();
break;
}
// The DB schema is little different in schema version 8x vs older versions
boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
// get form autofill artifacts
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
// get form address atifacts
bbartifacts.addAll(getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X));
dbFile.delete();
}
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, bbartifacts));
}
/**
* Extracts and returns autofill artifacts from the given database file
*
* @param webDataFile - the database file in the data source
* @param dbFilePath - path to a temporary file where the DB file is extracted
* @param isSchemaV8X - indicates of the DB schema version is 8X or greater
*
* @return collection of TSK_WEB_FORM_AUTOFILL artifacts
*/
private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
// The DB Schema is little different in version 8x vs older versions
String autoFillquery = (isSchemaV8X) ? AUTOFILL_QUERY_V8X
: AUTOFILL_QUERY;
List<HashMap<String, Object>> autofills = this.dbConnect(dbFilePath, autoFillquery);
logger.log(Level.INFO, "{0}- Now getting Autofill information from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, autofills.size()}); //NON-NLS
for (HashMap<String, Object> result : autofills) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
// extract all common attributes
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
(Integer.valueOf(result.get("count").toString())))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
Long.valueOf(result.get("date_created").toString()))); //NON-NLS
// get schema version specific attributes
if (isSchemaV8X) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
}
// Add an artifact
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
if (bbart != null) {
this.indexArtifact(bbart);
bbartifacts.add(bbart);
}
}
// return all extracted artifacts
return bbartifacts;
}
/**
* Extracts and returns autofill form address artifacts from the given database file
*
* @param webDataFile - the database file in the data source
* @param dbFilePath - path to a temporary file where the DB file is extracted
* @param isSchemaV8X - indicates of the DB schema version is 8X or greater
*
* @return collection of TSK_WEB_FORM_ADDRESS artifacts
*/
private Collection<BlackboardArtifact> getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
: WEBFORM_ADDRESS_QUERY;
// Get Web form addresses
List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
for (HashMap<String, Object> result : addresses) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
// get name fields
String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
String middle_name = result.get("middle_name").toString() != null ? result.get("middle_name").toString() : "";
String last_name = result.get("last_name").toString() != null ? result.get("last_name").toString() : "";
// get email and phone
String email_Addr = result.get("email").toString() != null ? result.get("email").toString() : "";
String phone_number = result.get("number").toString() != null ? result.get("number").toString() : "";
// Get the address fields
String city = result.get("city").toString() != null ? result.get("city").toString() : "";
String state = result.get("state").toString() != null ? result.get("state").toString() : "";
String zipcode = result.get("zipcode").toString() != null ? result.get("zipcode").toString() : "";
String country_code = result.get("country_code").toString() != null ? result.get("country_code").toString() : "";
// schema version specific fields
String full_name = "";
String street_address = "";
long date_modified = 0;
int use_count = 0;
long use_date = 0;
if (isSchemaV8X) {
full_name = result.get("full_name").toString() != null ? result.get("full_name").toString() : "";
street_address = result.get("street_address").toString() != null ? result.get("street_address").toString() : "";
date_modified = result.get("date_modified").toString() != null ? Long.valueOf(result.get("date_modified").toString()) : 0;
use_count = result.get("use_count").toString() != null ? Integer.valueOf(result.get("use_count").toString()) : 0;
use_date = result.get("use_date").toString() != null ? Long.valueOf(result.get("use_date").toString()) : 0;
} else {
String address_line_1 = result.get("address_line_1").toString() != null ? result.get("street_address").toString() : "";
String address_line_2 = result.get("address_line_2").toString() != null ? result.get("address_line_2").toString() : "";
street_address = String.join(" ", address_line_1, address_line_2);
}
// If an email address is found, create an account instance for it
if (email_Addr != null && !email_Addr.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email_Addr, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Chrome WebData file '%s' .",
email_Addr, webDataFile.getName()), ex); //NON-NLS
}
}
// If a phone number is found, create an account instance for it
if (phone_number != null && !phone_number.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phone_number, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), webDataFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating phone account instance for '%s' from Chrome WebData file '%s' .",
phone_number, webDataFile.getName()), ex); //NON-NLS
}
}
// Create atrributes from extracted fields
if (full_name == null || full_name.isEmpty()) {
full_name = String.join(" ", first_name, middle_name, last_name);
}
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
full_name)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
email_Addr)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
phone_number)); //NON-NLS
String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
locationAddress)); //NON-NLS
if (date_modified > 0) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
date_modified)); //NON-NLS
}
if (use_count > 0 ){
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
use_count)); //NON-NLS
}
if (use_date > 0) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
use_date)); //NON-NLS
}
// Create artifact
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS, webDataFile, bbattributes);
if (bbart != null) {
this.indexArtifact(bbart);
bbartifacts.add(bbart);
}
}
// return all extracted artifacts
return bbartifacts;
}
private boolean isChromePreVersion30(String temps) {
String query = "PRAGMA table_info(downloads)"; //NON-NLS
List<HashMap<String, Object>> columns = this.dbConnect(temps, query);

View File

@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2012-2018 Basis Technology Corp.
* Copyright 2012-2019 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com
@ -22,17 +22,30 @@
*/
package org.sleuthkit.autopsy.recentactivity;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
@ -41,6 +54,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -56,12 +70,22 @@ class Firefox extends Extract {
private static final Logger logger = Logger.getLogger(Firefox.class.getName());
private static final String PLACE_URL_PREFIX = "place:";
private static final String HISTORY_QUERY = "SELECT moz_historyvisits.id,url,title,visit_count,(visit_date/1000000) AS visit_date,from_visit,(SELECT url FROM moz_places WHERE id=moz_historyvisits.from_visit) as ref FROM moz_places, moz_historyvisits WHERE moz_places.id = moz_historyvisits.place_id AND hidden = 0"; //NON-NLS
private static final String HISTORY_QUERY = "SELECT moz_historyvisits.id, url, title, visit_count,(visit_date/1000000) AS visit_date,from_visit,"
+ "(SELECT url FROM moz_historyvisits history, moz_places places where history.id = moz_historyvisits.from_visit and history.place_id = places.id ) as ref "
+ "FROM moz_places, moz_historyvisits "
+ "WHERE moz_places.id = moz_historyvisits.place_id "
+ "AND hidden = 0"; //NON-NLS
private static final String COOKIE_QUERY = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies"; //NON-NLS
private static final String COOKIE_QUERY_V3 = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies"; //NON-NLS
private static final String BOOKMARK_QUERY = "SELECT fk, moz_bookmarks.title, url, (moz_bookmarks.dateAdded/1000000) AS dateAdded FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id"; //NON-NLS
private static final String DOWNLOAD_QUERY = "SELECT target, source,(startTime/1000000) AS startTime, maxBytes FROM moz_downloads"; //NON-NLS
private static final String DOWNLOAD_QUERY_V24 = "SELECT url, content AS target, (lastModified/1000000) AS lastModified FROM moz_places, moz_annos WHERE moz_places.id = moz_annos.place_id AND moz_annos.anno_attribute_id = 3"; //NON-NLS
private static final String DOWNLOAD_QUERY_V24 = "SELECT url, content AS target, (lastModified/1000000) AS lastModified "
+ " FROM moz_places, moz_annos, moz_anno_attributes "
+ " WHERE moz_places.id = moz_annos.place_id"
+ " AND moz_annos.anno_attribute_id = moz_anno_attributes.id"
+ " AND moz_anno_attributes.name='downloads/destinationFileURI'"; //NON-NLS
private static final String FORMHISTORY_QUERY = "SELECT fieldname, value FROM moz_formhistory";
private static final String FORMHISTORY_QUERY_V64 = "SELECT fieldname, value, timesUsed, firstUsed, lastUsed FROM moz_formhistory";
private final IngestServices services = IngestServices.getInstance();
private Content dataSource;
private IngestJobContext context;
@ -79,6 +103,8 @@ class Firefox extends Extract {
this.getBookmark();
this.getDownload();
this.getCookie();
this.getFormsHistory();
this.getAutofillProfiles();
}
private void getHistory() {
@ -650,6 +676,320 @@ class Firefox extends Extract {
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
}
/**
* Gets data from formshistory.sqlite database.
* Parses and creates artifacts.
*/
private void getFormsHistory() {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> formHistoryFiles;
// Some fields are just noisy and can me excluded
Set<String> excludedFieldNames = new HashSet<>(Arrays.asList(
"it", // some kind of timestamp
"ts" // some kind of timestamp
));
try {
formHistoryFiles = fileManager.findFiles(dataSource, "formhistory.sqlite", "Firefox"); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errFetchingFiles");
logger.log(Level.WARNING, msg);
this.addErrorMessage(this.getName() + ": " + msg);
return;
}
if (formHistoryFiles.isEmpty()) {
String msg = NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.noFilesFound");
logger.log(Level.INFO, msg);
return;
}
dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0;
for (AbstractFile formHistoryFile : formHistoryFiles) {
if (formHistoryFile.getSize() == 0) {
continue;
}
String fileName = formHistoryFile.getName();
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
try {
ContentUtils.writeToFile(formHistoryFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading Firefox web history artifacts file '%s' (id=%d).",
fileName, formHistoryFile.getId()), ex); //NON-NLS
this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
fileName));
continue;
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
tempFilePath, fileName, formHistoryFile.getId()), ex); //NON-NLS
this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
fileName));
continue;
}
File dbFile = new File(tempFilePath);
if (context.dataSourceIngestIsCancelled()) {
dbFile.delete();
break;
}
// The table schema is a little different in newer version of Firefox
boolean isFirefoxV64 = Util.checkColumn("timesUsed", "moz_formhistory", tempFilePath);
String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
logger.log(Level.INFO, "{0} - Now getting history from {1} with {2} artifacts identified.", new Object[]{moduleName, tempFilePath, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
String fieldName = ((result.get("fieldname").toString() != null) ? result.get("fieldname").toString() : "");
// filter out unuseful values
if (excludedFieldNames.contains(fieldName.toLowerCase())) {
continue;
}
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
fieldName)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
// Newer versions of firefox have additional columns
if (isFirefoxV64) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
(Long.valueOf(result.get("firstUsed").toString()) / 1000000))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
(Long.valueOf(result.get("lastUsed").toString()) / 1000000))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
(Integer.valueOf(result.get("timesUsed").toString())))); //NON-NLS
}
// Add artifact
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes);
if (bbart != null) {
this.indexArtifact(bbart);
bbartifacts.add(bbart);
}
}
++j;
dbFile.delete();
}
services.fireModuleDataEvent(new ModuleDataEvent(
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, bbartifacts));
}
/**
* Gets data from autofill-profiles.json file.
* Parses file and makes artifacts.
*
*/
private void getAutofillProfiles() {
FileManager fileManager = currentCase.getServices().getFileManager();
List<AbstractFile> autofillProfilesFiles;
try {
autofillProfilesFiles = fileManager.findFiles(dataSource, "autofill-profiles.json", "Firefox"); //NON-NLS
} catch (TskCoreException ex) {
String msg = NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errGettingFiles");
logger.log(Level.SEVERE, msg, ex);
this.addErrorMessage(this.getName() + ": " + msg);
return;
}
if (autofillProfilesFiles.isEmpty()) {
logger.log(Level.INFO, "Didn't find any Firefox Autofill Profiles files."); //NON-NLS
return;
}
dataFound = true;
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
int j = 0;
while (j < autofillProfilesFiles.size()) {
AbstractFile profileFile = autofillProfilesFiles.get(j++);
if (profileFile.getSize() == 0) {
continue;
}
String temps = RAImageIngestModule.getRATempPath(currentCase, "Firefox") + File.separator + profileFile.getName() + j + ".json"; //NON-NLS
try {
ContentUtils.writeToFile(profileFile, new File(temps), context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
profileFile.getName(), profileFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
this.getName(), profileFile.getName()));
continue;
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
temps, profileFile.getName(), profileFile.getId()), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
this.getName(), profileFile.getName()));
continue;
}
logger.log(Level.INFO, "{0}- Now getting Bookmarks from {1}", new Object[]{moduleName, temps}); //NON-NLS
File dbFile = new File(temps);
if (context.dataSourceIngestIsCancelled()) {
dbFile.delete();
break;
}
FileReader tempReader;
try {
tempReader = new FileReader(temps);
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Error while trying to read the Autofill profiles json file for Firefox.", ex); //NON-NLS
this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
profileFile.getName()));
continue;
}
final JsonParser parser = new JsonParser();
JsonObject jsonRootObject;
JsonArray jAddressesArray;
try {
jsonRootObject = parser.parse(tempReader).getAsJsonObject();
jAddressesArray = jsonRootObject.getAsJsonArray("addresses"); //NON-NLS
} catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
logger.log(Level.WARNING, "Error parsing Json for Firefox Autofill profiles.", ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
this.getName(), profileFile.getName()));
continue;
}
for (JsonElement result : jAddressesArray) {
JsonObject address = result.getAsJsonObject();
if (address == null) {
continue;
}
JsonElement nameEl = address.get("name"); //NON-NLS
String name = (nameEl != null) ? nameEl.getAsString() : "";
JsonElement emailEl = address.get("email"); //NON-NLS
String email = (emailEl != null) ? emailEl.getAsString() : "";
JsonElement telEl = address.get("tel"); //NON-NLS
String tel = (telEl != null) ? telEl.getAsString() : "";
JsonElement telCountryCodeEl = address.get("tel-country-code"); //NON-NLS
String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() : "";
JsonElement telNationalEl = address.get("tel-national"); //NON-NLS
String telNational = (telNationalEl != null) ? telNationalEl.getAsString() : "";
String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
JsonElement createdEl = address.get("timeCreated"); //NON-NLS
Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);
JsonElement lastusedEl = address.get("timeLastUsed"); //NON-NLS
Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0);
JsonElement timesUsedEl = address.get("timesUsed"); //NON-NLS
Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
JsonElement addressLine1El = address.get("address-line1"); //NON-NLS
String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() : "";
JsonElement addressLine2El = address.get("address-line2"); //NON-NLS
String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() : "";
JsonElement addressLine3El = address.get("address-line3"); //NON-NLS
String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() : "";
JsonElement postalCodeEl = address.get("postal-code"); //NON-NLS
String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() : "";
JsonElement countryEl = address.get("country"); //NON-NLS
String country = (countryEl != null) ? countryEl.getAsString() : "";
String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
try {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
name)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
email)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
phoneNumber)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
mailingAddress)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
datetimeCreated)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
datetimeLastUsed)); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
timesUsed)); //NON-NLS
BlackboardArtifact bbart = profileFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS);
// index the artifact for keyword search
if (bbart != null) {
bbart.addAttributes(bbattributes);
this.indexArtifact(bbart);
bbartifacts.add(bbart);
}
// If an email address is found, create an account instance for it
if (email != null && !email.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating email account instance for '%s' from Firefox profiles file '%s' .",
email, profileFile.getName()), ex); //NON-NLS
}
}
// If a phone number is found, create an account instance for it
if (phoneNumber != null && !phoneNumber.isEmpty()) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phoneNumber, NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"), profileFile);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating phone number account instance for '%s' from Chrome profiles file '%s' .",
phoneNumber, profileFile.getName()), ex); //NON-NLS
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
this.getName(), profileFile.getName()));
}
}
dbFile.delete();
}
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS, bbartifacts));
}
/**
* Extract the domain from the supplied URL. This method does additional
* checks to detect invalid URLs.
@ -672,4 +1012,72 @@ class Firefox extends Extract {
return NetworkUtils.extractDomain(url);
}
/**
* Returns a phone number based on input number or components of phone number.
*
* @param tel full number, if available
* @param telCountryCode country code
* @param telNational full national number
*
* @return phone number, or an empty string if no number can be deciphered from input
*/
private String makeTelNumber(String tel, String telCountryCode, String telNational) {
if (tel != null && !tel.isEmpty()) {
return tel;
}
if ((telCountryCode != null && !telCountryCode.isEmpty()) &&
(telNational != null && !telNational.isEmpty())) {
return telCountryCode + telNational;
}
return "";
}
/**
* Returns a full postal address from multiple address fields.
*
* @parm addressLine1
* @parm addressLine2
* @parm addressLine3
* @parm postalCode
* @parm country
*
* @return full address
*/
private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country ) {
String fullAddress = "";
fullAddress = appendAddressField(fullAddress, addressLine1 );
fullAddress = appendAddressField(fullAddress, addressLine2 );
fullAddress = appendAddressField(fullAddress, addressLine3 );
fullAddress = appendAddressField(fullAddress, postalCode );
fullAddress = appendAddressField(fullAddress, country );
return fullAddress;
}
/**
* Appends the given address field to given address, if not empty.
* Adds delimiter in between if needed.
*
* @param address
* @param addressfield
* @return updated address
*/
private String appendAddressField(String address, String addressfield) {
String updatedAddress = address;
if (addressfield != null && !addressfield.isEmpty()) {
if (!updatedAddress.isEmpty()) {
updatedAddress += ", ";
}
updatedAddress += addressfield;
}
return updatedAddress;
}
}