Conflicts:
	Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java
	Core/src/org/sleuthkit/autopsy/datamodel/FileTypeChildren.java
This commit is contained in:
Jeff Wallace 2013-09-17 12:53:50 -04:00
commit c81669ca61
53 changed files with 1362 additions and 1982 deletions

16
Core/autopsy-updates.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE module_updates PUBLIC "-//NetBeans//DTD Autoupdate Catalog 2.5//EN" "http://www.netbeans.org/dtds/autoupdate-catalog-2_7.dtd">
<module_updates timestamp="03/01/21/12/03/2008">
<!--notification element goes here for user pop-up. Attribute url is the URL to display along with the message contained between notification xml elements.
-->
<notification url="http://sleuthkit.org/autopsy">
Visit http://sleuthkit.org/autopsy to download the latest version of Autopsy.
</notification>
</module_updates>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.ingest.IngestConfigurator;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.event.ActionEvent;

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.ingest.IngestConfigurator;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
@ -28,6 +29,7 @@ import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
@ -39,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.ContentTypePanel.ContentType;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestDialog;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image;
@ -86,7 +89,14 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel<WizardDescriptor> {
this.action = action;
this.wizPanel = wizPanel;
ingestConfig = Lookup.getDefault().lookup(IngestConfigurator.class);
ingestConfig.setContext(AddImageWizardPanel3.class.getCanonicalName());
List<String> messages = ingestConfig.setContext(AddImageWizardPanel3.class.getCanonicalName());
if (messages.isEmpty() == false) {
StringBuilder warning = new StringBuilder();
for (String message : messages) {
warning.append(message).append("\n");
}
JOptionPane.showMessageDialog(null, warning.toString());
}
}
/**

View File

@ -236,7 +236,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
* @param configFilePath the path of the configuration file that's opened
* @throws CaseActionException
*/
static void open(String configFilePath) throws CaseActionException {
public static void open(String configFilePath) throws CaseActionException {
logger.log(Level.INFO, "Opening case.\nconfigFilePath: {0}", configFilePath);
try {

View File

@ -1,69 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2013 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;
import java.util.List;
import javax.swing.JPanel;
import org.sleuthkit.datamodel.Content;
/**
* Lookup interface for ingest configuration dialog
*/
public interface IngestConfigurator {
/**
* get JPanel container with the configurator
* @return
*/
JPanel getIngestConfigPanel();
/**
* set input Content to be configured for ingest
* @param inputContent content to be configured for ingest
*/
void setContent(List<Content> inputContent);
/**
* start ingest enqueing previously set image
*/
void start();
/**
* save configuration of lastly selected service
*/
void save();
/**
* reload the simple panel
*/
void reload();
/**
* find out if ingest is currently running
*
* @return true if ingest process is running, false otherwise
*/
boolean isIngestRunning();
/**
* Set the context for the configuration.
* @param context
*/
public void setContext(String context);
}

View File

@ -144,6 +144,8 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer
}
addRow(sb, "MD5", md5);
addRow(sb, "Internal ID", new Long(file.getId()).toString());
endTable(sb);
setText(sb.toString());
}

View File

@ -8,3 +8,5 @@ OpenIDE-Module-Long-Description=\
For more information, see http://www.sleuthkit.org/autopsy/
OpenIDE-Module-Name=Autopsy-Core
OpenIDE-Module-Short-Description=Autopsy Core Module
org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml
Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings=Autopsy Update Center

View File

@ -270,6 +270,13 @@
Services
====================================================== -->
<folder name="Services">
<folder name="AutoupdateType">
<file name="org_sleuthkit_autopsy_core_update_center.settings" url="org_sleuthkit_autopsy_core_update_centerSettings.xml">
<attr name="displayName" bundlevalue="org.sleuthkit.autopsy.core.Bundle#Services/AutoupdateType/org_sleuthkit_autopsy_core_update_center.settings"/>
<attr name="enabled" boolvalue="true"/>
<attr name="url" bundlevalue="org.sleuthkit.autopsy.core.Bundle#org_sleuthkit_autopsy_core_update_center"/>
</file>
</folder>
<file name="org-sleuthkit-autopsy-corecomponents-DataContentTopComponent.instance">
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.corecomponentinterfaces.DataContent"/>
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.corecomponents.DataContentTopComponent.getDefault"/>

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!--
To change this template, choose Tools | Templates
and open the template in the editor.
-->
<!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd">
<settings version="1.0">
<module name="org.sleuthkit.autopsy.core/8"/>
<instanceof class="org.openide.ServiceType"/>
<instanceof class="org.netbeans.modules.autoupdate.AutoupdateType"/>
<instanceof class="org.netbeans.modules.autoupdate.XMLAutoupdateType"/>
<instance class="org.netbeans.modules.autoupdate.XMLAutoupdateType" method="createXMLAutoupdateType"/>
</settings>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,8 +32,6 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.DataConversion;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.TskException;
/**
@ -41,11 +39,10 @@ import org.sleuthkit.datamodel.TskException;
*/
@ServiceProvider(service = DataContentViewer.class, position = 1)
public class DataContentViewerHex extends javax.swing.JPanel implements DataContentViewer {
private static long currentOffset = 0;
private static final long pageLength = 16384;
private final byte[] data = new byte[(int) pageLength];
private static int currentPage = 1;
private int totalPages;
private Content dataSource;
private static final Logger logger = Logger.getLogger(DataContentViewerHex.class.getName());
@ -243,39 +240,28 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
}// </editor-fold>//GEN-END:initComponents
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
//@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
currentOffset -= pageLength;
currentPage = currentPage - 1;
currentPageLabel.setText(Integer.toString(currentPage));
setDataView(dataSource, currentOffset);
setDataView(currentPage - 1);
}//GEN-LAST:event_prevPageButtonActionPerformed
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
//@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
currentOffset += pageLength;
currentPage = currentPage + 1;
currentPageLabel.setText(Integer.toString(currentPage));
setDataView(dataSource, currentOffset);
setDataView(currentPage + 1);
}//GEN-LAST:event_nextPageButtonActionPerformed
private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed
String pageNumberStr = goToPageTextField.getText();
int pageNumber = 0;
int maxPage = Math.round((dataSource.getSize() - 1) / pageLength) + 1;
try {
pageNumber = Integer.parseInt(pageNumberStr);
} catch (NumberFormatException ex) {
pageNumber = maxPage + 1;
pageNumber = totalPages + 1;
}
if (pageNumber > maxPage || pageNumber < 1) {
JOptionPane.showMessageDialog(this, "Please enter a valid page number between 1 and " + maxPage,
if (pageNumber > totalPages || pageNumber < 1) {
JOptionPane.showMessageDialog(this, "Please enter a valid page number between 1 and " + totalPages,
"Invalid page number", JOptionPane.WARNING_MESSAGE);
return;
}
currentOffset = (pageNumber - 1) * pageLength;
currentPage = pageNumber;
currentPageLabel.setText(Integer.toString(currentPage));
setDataView(dataSource, currentOffset);
setDataView(pageNumber);
}//GEN-LAST:event_goToPageTextFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem;
@ -296,30 +282,27 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
// End of variables declaration//GEN-END:variables
@Deprecated
public void setDataView(Content dataSource, long offset, boolean reset) {
if (reset) {
resetComponent();
return;
}
setDataView(dataSource, offset);
}
/**
* Sets the DataView (The tabbed panel)
*
* @param dataSource the content that want to be shown
* @param offset the starting offset
* @param page Page to display (1-based counting)
*/
private void setDataView(Content dataSource, long offset) {
if (dataSource == null) {
private void setDataView(int page) {
if (this.dataSource == null) {
return;
}
if (page == 0) {
return;
}
currentPage = page;
long offset = (currentPage - 1) * pageLength;
// change the cursor to "waiting cursor" for this operation
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
this.dataSource = dataSource;
String errorText = null;
int bytesRead = 0;
@ -327,7 +310,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
try {
bytesRead = dataSource.read(data, offset, pageLength); // read the data
} catch (TskException ex) {
errorText = "(offset " + currentOffset + "-" + (currentOffset + pageLength)
errorText = "(offset " + offset + "-" + (offset + pageLength)
+ " could not be read)";
logger.log(Level.WARNING, "Error while trying to show the hex content.", ex);
}
@ -335,27 +318,26 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
// set the data on the bottom and show it
if (bytesRead <= 0) {
errorText = "(offset " + currentOffset + "-" + (currentOffset + pageLength)
errorText = "(offset " + offset + "-" + (offset + pageLength)
+ " could not be read)";
}
// disable or enable the next button
if ((errorText != null) && (offset + pageLength < dataSource.getSize())) {
if ((errorText == null) && (currentPage < totalPages)) {
nextPageButton.setEnabled(true);
} else {
}
else {
nextPageButton.setEnabled(false);
}
if ((offset == 0) || (errorText == null)) {
prevPageButton.setEnabled(false);
currentPage = 1; // reset the page number
} else {
if ((errorText == null) && (currentPage > 1)) {
prevPageButton.setEnabled(true);
}
else {
prevPageButton.setEnabled(false);
}
int totalPage = Math.round((dataSource.getSize() - 1) / pageLength) + 1;
totalPageLabel.setText(Integer.toString(totalPage));
currentPageLabel.setText(Integer.toString(currentPage));
setComponentsVisibility(true); // shows the components that not needed
@ -384,8 +366,15 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
resetComponent();
return;
}
dataSource = content;
totalPages = 0;
if (dataSource.getSize() > 0) {
totalPages = Math.round((dataSource.getSize() - 1) / pageLength) + 1;
}
totalPageLabel.setText(Integer.toString(totalPages));
this.setDataView(content, 0);
this.setDataView(1);
}
@Override
@ -408,7 +397,6 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
public void resetComponent() {
// clear / reset the fields
currentPage = 1;
currentOffset = 0;
this.dataSource = null;
currentPageLabel.setText("");
totalPageLabel.setText("");

View File

@ -25,9 +25,7 @@ import java.util.Arrays;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.SwingUtilities;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
@ -41,16 +39,15 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
@ServiceProvider(service = DataContentViewer.class, position = 5)
})
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
private String[] IMAGES; // use javafx supported
private static final String[] VIDEOS = new String[]{".swf", ".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final String[] AUDIOS = new String[]{".mp3", ".wav", ".wma"};
private static final String[] AUDIO_EXTENSIONS = new String[]{".mp3", ".wav", ".wma"};
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
private AbstractFile lastFile;
//UI
private final MediaViewVideoPanel videoPanel;
private final String[] videoExtensions; // get them from the panel
private String[] imageExtensions; // use javafx supported
private final MediaViewImagePanel imagePanel;
private boolean videoPanelInited;
private boolean imagePanelInited;
@ -72,6 +69,8 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
videoPanelInited = videoPanel.isInited();
imagePanelInited = imagePanel.isInited();
videoExtensions = videoPanel.getExtensions();
customizeComponents();
logger.log(Level.INFO, "Created MediaView instance: " + this);
}
@ -80,12 +79,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
//initialize supported image types
//TODO use mime-types instead once we have support
String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
IMAGES = new String[fxSupportedImagesSuffixes.length];
imageExtensions = new String[fxSupportedImagesSuffixes.length];
//logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) {
String suffix = fxSupportedImagesSuffixes[i];
//logger.log(Level.INFO, "suffix: " + suffix);
IMAGES[i] = "." + suffix;
imageExtensions[i] = "." + suffix;
}
add(imagePanel, IMAGE_VIEWER_LAYER);
@ -132,11 +131,11 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
final Dimension dims = DataContentViewerMedia.this.getSize();
if (imagePanelInited && containsExt(file.getName(), IMAGES)) {
if (imagePanelInited && containsExt(file.getName(), imageExtensions)) {
imagePanel.showImageFx(file, dims);
this.switchPanels(false);
} else if (videoPanelInited
&& (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) {
&& (containsExt(file.getName(), videoExtensions) || containsExt(file.getName(), AUDIO_EXTENSIONS))) {
videoPanel.setupVideo(file, dims);
switchPanels(true);
}
@ -182,8 +181,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
@Override
public void resetComponent() {
// No need to reset the video panel. It resets itself when a node is
// set.
videoPanel.reset();
lastFile = null;
}
@ -203,13 +201,13 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
}
String name = file.getName().toLowerCase();
if (imagePanelInited && containsExt(name, IMAGES)) {
if (imagePanelInited && containsExt(name, imageExtensions)) {
return true;
} //for gstreamer formats, check if initialized first, then
//support audio formats, and video formats
else if (videoPanelInited && videoPanel.isInited()
&& (containsExt(name, AUDIOS)
|| (containsExt(name, VIDEOS)))) {
&& (containsExt(name, AUDIO_EXTENSIONS)
|| (containsExt(name, videoExtensions)))) {
return true;
}
@ -227,7 +225,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
String name = file.getName().toLowerCase();
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
if (containsExt(name, VIDEOS) && deleted) {
if (containsExt(name, videoExtensions) && deleted) {
return 0;
} else {
return 7;

View File

@ -194,7 +194,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
// as DataResultViewer service providers when DataResultViewers are updated
// to better handle the ExplorerManager sharing implemented to support actions that operate on
// multiple selected nodes.
addDataResultViewer(new DataResultViewerTable(this.explorerManager));
addDataResultViewer(new DataResultViewerTable(this.explorerManager));
addDataResultViewer(new DataResultViewerThumbnail(this.explorerManager));
// Find all DataResultViewer service providers and add them to the tabbed pane.
@ -305,9 +305,8 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
int childrenCount = selectedNode.getChildren().getNodesCount();
this.numberMatchLabel.setText(Integer.toString(childrenCount));
}
this.numberMatchLabel.setVisible(true);
this.numberMatchLabel.setVisible(true);
resetTabs(selectedNode);
@ -331,6 +330,28 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
}
++drvC;
}
// if the current tab is no longer enabled, then find one that is
boolean hasViewerEnabled = true;
int currentActiveTab = this.dataResultTabbedPanel.getSelectedIndex();
if ((currentActiveTab == -1) || (dataResultTabbedPanel.isEnabledAt(currentActiveTab) == false)) {
hasViewerEnabled = false;
for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) {
if (dataResultTabbedPanel.isEnabledAt(i)) {
currentActiveTab = i;
hasViewerEnabled = true;
break;
}
}
if (hasViewerEnabled) {
dataResultTabbedPanel.setSelectedIndex(currentActiveTab);
}
}
if (hasViewerEnabled) {
viewers.get(currentActiveTab).setNode(selectedNode);
}
}
@Override
@ -387,6 +408,10 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
}
/**
* why does this take a Node as parameter and then ignore it?
*
*
*
* Resets the tabs based on the selected Node. If the selected node is null
* or not supported, disable that tab as well.
*

View File

@ -86,7 +86,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
ov.setAllowedDragActions(DnDConstants.ACTION_NONE);
ov.setAllowedDropActions(DnDConstants.ACTION_NONE);
// only allow one item to be selected at a time
ov.getOutline().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// don't show the root node

View File

@ -85,7 +85,6 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private void initialize() {
initComponents();
// only allow one item to be selected at a time
((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
curPage = -1;

View File

@ -80,6 +80,7 @@ import org.sleuthkit.autopsy.core.Installer;
})
public class FXVideoPanel extends MediaViewVideoPanel {
private static final String[] EXTENSIONS = new String[]{".swf", ".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
private boolean fxInited = false;
// FX Components
@ -794,4 +795,9 @@ public class FXVideoPanel extends MediaViewVideoPanel {
// return frames;
// }
// }
@Override
public String[] getExtensions() {
return EXTENSIONS;
}
}

View File

@ -67,6 +67,8 @@ import org.sleuthkit.datamodel.TskData;
})
public class GstVideoPanel extends MediaViewVideoPanel {
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
private boolean gstInited;
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
@ -179,6 +181,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
@Override
void setupVideo(final AbstractFile file, final Dimension dims) {
reset();
infoLabel.setText("");
currentFile = file;
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
@ -437,7 +440,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
videoPanel = new javax.swing.JPanel();
@ -769,4 +772,9 @@ public class GstVideoPanel extends MediaViewVideoPanel {
});
}
}
@Override
public String[] getExtensions() {
return EXTENSIONS;
}
}

View File

@ -115,4 +115,9 @@ public abstract class MediaViewVideoPanel extends JPanel implements FrameCapture
* @param dims dimension of the parent window
*/
abstract void setupVideo(final AbstractFile file, final Dimension dims);
/**
* Return the extensions supported by this video panel.
*/
abstract public String[] getExtensions();
}

View File

@ -122,8 +122,8 @@ abstract class AbstractContentChildren<T> extends Keys<T> {
}
@Override
public AbstractNode visit(SearchFilters sf) {
return new SearchFiltersNode(sf.getSleuthkitCase(), null);
public AbstractNode visit(FileTypeExtensionFilters sf) {
return new FileTypesNode(sf.getSleuthkitCase(), null);
}
@Override

View File

@ -57,7 +57,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
* Return the content data associated with this node
* @return the content object wrapped by this node
*/
public Content getContent() {
public T getContent() {
return content;
}

View File

@ -26,13 +26,13 @@ public interface AutopsyItemVisitor<T> {
T visit(ExtractedContent ec);
T visit(SearchFilters sf);
T visit(FileTypeExtensionFilters sf);
T visit(SearchFilters.FileSearchFilter fsf);
T visit(FileTypeExtensionFilters.RootFilter fsf);
T visit(SearchFilters.DocumentFilter df);
T visit(FileTypeExtensionFilters.DocumentFilter df);
T visit(SearchFilters.ExecutableFilter ef);
T visit(FileTypeExtensionFilters.ExecutableFilter ef);
T visit(RecentFiles rf);
@ -70,22 +70,22 @@ public interface AutopsyItemVisitor<T> {
}
@Override
public T visit(SearchFilters sf) {
public T visit(FileTypeExtensionFilters sf) {
return defaultVisit(sf);
}
@Override
public T visit(SearchFilters.FileSearchFilter fsf) {
public T visit(FileTypeExtensionFilters.RootFilter fsf) {
return defaultVisit(fsf);
}
@Override
public T visit(SearchFilters.DocumentFilter df) {
public T visit(FileTypeExtensionFilters.DocumentFilter df) {
return defaultVisit(df);
}
@Override
public T visit(SearchFilters.ExecutableFilter ef) {
public T visit(FileTypeExtensionFilters.ExecutableFilter ef) {
return defaultVisit(ef);
}

View File

@ -53,7 +53,7 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(ExtractedContentNode ecn);
T visit(FileSearchFilterNode fsfn);
T visit(FileTypeNode fsfn);
T visit(DeletedContentNode dcn);
@ -63,7 +63,7 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(FileSizeNode fsn);
T visit(SearchFiltersNode sfn);
T visit(FileTypesNode sfn);
T visit(RecentFilesNode rfn);
@ -155,7 +155,7 @@ public interface DisplayableItemNodeVisitor<T> {
}
@Override
public T visit(FileSearchFilterNode fsfn) {
public T visit(FileTypeNode fsfn) {
return defaultVisit(fsfn);
}
@ -180,7 +180,7 @@ public interface DisplayableItemNodeVisitor<T> {
}
@Override
public T visit(SearchFiltersNode sfn) {
public T visit(FileTypesNode sfn) {
return defaultVisit(sfn);
}

View File

@ -25,6 +25,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile;
@ -37,16 +38,16 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Children factory for the file by type view in dir tree
* Children factory for a specific file type - does the database query.
*/
class FileSearchFilterChildren extends ChildFactory<Content> {
class FileTypeChildren extends ChildFactory<Content> {
private SleuthkitCase skCase;
private SearchFilters.SearchFilterInterface filter;
private static final Logger logger = Logger.getLogger(FileSearchFilterChildren.class.getName());
// private final static int MAX_OBJECTS = 2000;
private FileTypeExtensionFilters.SearchFilterInterface filter;
private static final Logger logger = Logger.getLogger(FileTypeChildren.class.getName());
//private final static int MAX_OBJECTS = 2000;
public FileSearchFilterChildren(SearchFilters.SearchFilterInterface filter, SleuthkitCase skCase) {
public FileTypeChildren(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) {
this.filter = filter;
this.skCase = skCase;
}
@ -59,7 +60,7 @@ class FileSearchFilterChildren extends ChildFactory<Content> {
private String createQuery(){
String query = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+ " AND (known IS NULL OR known != 1) AND (0";
+ " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ") AND (0";
for(String s : filter.getFilter()){
query += " OR name LIKE '%" + s + "'";
}
@ -69,8 +70,8 @@ class FileSearchFilterChildren extends ChildFactory<Content> {
}
private List<? extends Content> runQuery(){
List<? extends Content> list = new ArrayList<>();
private List<AbstractFile> runQuery(){
List<AbstractFile> list = new ArrayList<>();
try {
list = skCase.findAllFilesWhere(createQuery());
} catch (TskCoreException ex) {

View File

@ -25,11 +25,12 @@ import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Filters database results by file extension.
*/
public class SearchFilters implements AutopsyVisitableItem {
public class FileTypeExtensionFilters implements AutopsyVisitableItem {
private SleuthkitCase skCase;
public enum FileSearchFilter implements AutopsyVisitableItem,SearchFilterInterface {
// root node filters
public enum RootFilter implements AutopsyVisitableItem,SearchFilterInterface {
TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", "Images", FileTypeExtensions.getImageExtensions()),
TSK_VIDEO_FILTER(1, "TSK_VIDEO_FILTER", "Videos", FileTypeExtensions.getVideoExtensions()),
TSK_AUDIO_FILTER(2, "TSK_AUDIO_FILTER", "Audio", FileTypeExtensions.getAudioExtensions()),
@ -42,7 +43,7 @@ public class SearchFilters implements AutopsyVisitableItem {
private String displayName;
private List<String> filter;
private FileSearchFilter(int id, String name, String displayName, List<String> filter){
private RootFilter(int id, String name, String displayName, List<String> filter){
this.id = id;
this.name = name;
this.displayName = displayName;
@ -75,6 +76,7 @@ public class SearchFilters implements AutopsyVisitableItem {
}
}
// document sub-node filters
public enum DocumentFilter implements AutopsyVisitableItem,SearchFilterInterface {
AUT_DOC_HTML(0, "AUT_DOC_HTML", "HTML", Arrays.asList(".htm", ".html")),
AUT_DOC_OFFICE(1, "AUT_DOC_OFFICE", "Office", Arrays.asList(".doc", ".docx",
@ -122,6 +124,7 @@ public class SearchFilters implements AutopsyVisitableItem {
}
// executable sub-node filters
public enum ExecutableFilter implements AutopsyVisitableItem,SearchFilterInterface {
ExecutableFilter_EXE(0, "ExecutableFilter_EXE", ".exe", Arrays.asList(".exe")),
ExecutableFilter_DLL(1, "ExecutableFilter_DLL", ".dll", Arrays.asList(".dll")),
@ -167,7 +170,7 @@ public class SearchFilters implements AutopsyVisitableItem {
}
}
public SearchFilters(SleuthkitCase skCase){
public FileTypeExtensionFilters(SleuthkitCase skCase){
this.skCase = skCase;
}

View File

@ -1,3 +1,21 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2013 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.datamodel;
import java.util.Arrays;

View File

@ -24,15 +24,15 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Node for the file search filter
* Node for a specific file type / extension
*/
public class FileSearchFilterNode extends DisplayableItemNode {
public class FileTypeNode extends DisplayableItemNode {
SearchFilters.SearchFilterInterface filter;
FileTypeExtensionFilters.SearchFilterInterface filter;
SleuthkitCase skCase;
FileSearchFilterNode(SearchFilters.SearchFilterInterface filter, SleuthkitCase skCase) {
super(Children.create(new FileSearchFilterChildren(filter, skCase), true), Lookups.singleton(filter.getDisplayName()));
FileTypeNode(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) {
super(Children.create(new FileTypeChildren(filter, skCase), true), Lookups.singleton(filter.getDisplayName()));
this.filter = filter;
this.skCase = skCase;
@ -40,7 +40,7 @@ public class FileSearchFilterNode extends DisplayableItemNode {
super.setName(filter.getName());
//get count of children without preloading all children nodes
final long count = new FileSearchFilterChildren(filter, skCase).calculateItems();
final long count = new FileTypeChildren(filter, skCase).calculateItems();
//final long count = getChildren().getNodesCount(true);
super.setDisplayName(filter.getDisplayName() + " (" + count + ")");

View File

@ -0,0 +1,75 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 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.datamodel;
import java.util.Arrays;
import java.util.List;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.datamodel.FileTypeExtensionFilters.RootFilter;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
*
*/
class FileTypesChildren extends ChildFactory<FileTypeExtensionFilters.SearchFilterInterface> {
private SleuthkitCase skCase;
private FileTypeExtensionFilters.RootFilter filter;
/**
*
* @param skCase
* @param filter Is null for root node
*/
public FileTypesChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter) {
this.skCase = skCase;
this.filter = filter;
}
@Override
protected boolean createKeys(List<FileTypeExtensionFilters.SearchFilterInterface> list) {
// root node
if (filter == null) {
list.addAll(Arrays.asList(RootFilter.values()));
}
// document and executable has another level of nodes
else if (filter.equals(RootFilter.TSK_DOCUMENT_FILTER) ){
list.addAll(Arrays.asList(FileTypeExtensionFilters.DocumentFilter.values()));
}
else if (filter.equals(RootFilter.TSK_EXECUTABLE_FILTER) ){
list.addAll(Arrays.asList(FileTypeExtensionFilters.ExecutableFilter.values()));
}
return true;
}
@Override
protected Node createNodeForKey(FileTypeExtensionFilters.SearchFilterInterface key){
// make new nodes for the sub-nodes
if(key.getName().equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER.getName())){
return new FileTypesNode(skCase, FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER);
}
else if(key.getName().equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER.getName())){
return new FileTypesNode(skCase, FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER);
}
else {
return new FileTypeNode(key, skCase);
}
}
}

View File

@ -24,19 +24,27 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Node for search filter
* Node for extension/file type filter view
*/
public class SearchFiltersNode extends DisplayableItemNode {
public class FileTypesNode extends DisplayableItemNode {
private static final String FNAME = "File Types";
private SleuthkitCase skCase;
SearchFiltersNode(SleuthkitCase skCase, SearchFilters.FileSearchFilter filter) {
super(Children.create(new SearchFiltersChildren(skCase, filter), true), Lookups.singleton(filter == null ? FNAME : filter.getName()));
/**
*
* @param skCase
* @param filter null to display root node of file type tree, pass in something to provide a sub-node.
*/
FileTypesNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter) {
super(Children.create(new FileTypesChildren(skCase, filter), true), Lookups.singleton(filter == null ? FNAME : filter.getName()));
// root node of tree
if (filter == null) {
super.setName(FNAME);
super.setDisplayName(FNAME);
} else {
}
// sub-node in file tree (i.e. documents, exec, etc.)
else {
super.setName(filter.getName());
super.setDisplayName(filter.getDisplayName());
}

View File

@ -30,7 +30,7 @@ public class NodeProperty extends PropertySupport.ReadOnly {
private Object value;
@SuppressWarnings({"unchecked"})
NodeProperty(String name, String displayName, String desc, Object value) {
public NodeProperty(String name, String displayName, String desc, Object value) {
super(name, value.getClass(), displayName, desc);
setValue("suppressCustomEditor", Boolean.TRUE); // remove the "..." (editing) button
this.value = value;

View File

@ -1,68 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 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.datamodel;
import java.util.Arrays;
import java.util.List;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.datamodel.SearchFilters.FileSearchFilter;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
*
*/
class SearchFiltersChildren extends ChildFactory<SearchFilters.SearchFilterInterface> {
private SleuthkitCase skCase;
private SearchFilters.FileSearchFilter filter;
public SearchFiltersChildren(SleuthkitCase skCase, SearchFilters.FileSearchFilter filter) {
this.skCase = skCase;
this.filter = filter;
}
@Override
protected boolean createKeys(List<SearchFilters.SearchFilterInterface> list) {
if (filter == null) {
list.addAll(Arrays.asList(FileSearchFilter.values()));
}
else if (filter.equals(FileSearchFilter.TSK_DOCUMENT_FILTER) ){
list.addAll(Arrays.asList(SearchFilters.DocumentFilter.values()));
}
else if (filter.equals(FileSearchFilter.TSK_EXECUTABLE_FILTER) ){
list.addAll(Arrays.asList(SearchFilters.ExecutableFilter.values()));
}
return true;
}
@Override
protected Node createNodeForKey(SearchFilters.SearchFilterInterface key){
if(key.getName().equals(SearchFilters.FileSearchFilter.TSK_DOCUMENT_FILTER.getName())){
return new SearchFiltersNode(skCase, SearchFilters.FileSearchFilter.TSK_DOCUMENT_FILTER);
}
else if(key.getName().equals(SearchFilters.FileSearchFilter.TSK_EXECUTABLE_FILTER.getName())){
return new SearchFiltersNode(skCase, SearchFilters.FileSearchFilter.TSK_EXECUTABLE_FILTER);
}
else {
return new FileSearchFilterNode(key, skCase);
}
}
}

View File

@ -34,7 +34,7 @@ public class ViewsNode extends DisplayableItemNode {
public ViewsNode(SleuthkitCase sleuthkitCase) {
super(new RootContentChildren(Arrays.asList(
new SearchFilters(sleuthkitCase),
new FileTypeExtensionFilters(sleuthkitCase),
new RecentFiles(sleuthkitCase),
new DeletedContent(sleuthkitCase),
new FileSize(sleuthkitCase)

View File

@ -219,10 +219,16 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
// change the cursor to "waiting cursor" for this operation
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// update the back and forward List
// the end is the current place,
String[] currentNodePath = backList.pollLast();
String[] newCurrentNodePath = backList.peekLast();
forwardList.addLast(currentNodePath);
forwardButton.setEnabled(true);
/* We peek instead of poll because we use its existence
* in the list later on so that we do not reset the forward list
* after the selection occurs. */
String[] newCurrentNodePath = backList.peekLast();
// enable / disable the back and forward button
if (backList.size() > 1) {
@ -230,39 +236,31 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} else {
backButton.setEnabled(false);
}
this.forwardButton.setEnabled(true);
// update the selection on directory tree
setSelectedNode(newCurrentNodePath, null);
this.setCursor(null);
}//GEN-LAST:event_backButtonActionPerformed
private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
// change the cursor to "waiting cursor" for this operation
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// try {
// update the back and forward List
//int newCurrentIndex = forwardList.size() - 1;
String[] newCurrentNodePath = forwardList.pollLast();
//forwardList.remove(newCurrentIndex);
backList.addLast(newCurrentNodePath);
// enable / disable the back and forward button
String[] newCurrentNodePath = forwardList.pollLast();
if (!forwardList.isEmpty()) {
forwardButton.setEnabled(true);
} else {
forwardButton.setEnabled(false);
}
this.backButton.setEnabled(true);
backList.addLast(newCurrentNodePath);
backButton.setEnabled(true);
// update the selection on directory tree
setSelectedNode(newCurrentNodePath, null);
this.setCursor(null);
}//GEN-LAST:event_forwardButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton;
@ -337,7 +335,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root
} else {
// if there's at least one image, load the image and open the top component
List<Object> items = new ArrayList<Object>();
List<Object> items = new ArrayList<>();
final SleuthkitCase tskCase = currentCase.getSleuthkitCase();
items.add(new DataSources(tskCase));
items.add(new Views(tskCase));
@ -375,7 +373,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
((BeanTreeView) this.jScrollPane1).setRootVisible(false); // hide the root
// Reset the forward and back lists because we're resetting the root context
resetHistoryListAndButtons();
resetHistory();
Children childNodes = em.getRootContext().getChildren();
TreeView tree = getTree();
@ -533,7 +531,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
// case opened
if (newValue != null) {
resetHistoryListAndButtons();
resetHistory();
}
} // if the image is added to the case
else if (changed.equals(Case.CASE_ADD_DATA_SOURCE)) {
@ -671,31 +669,53 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
});
// update the back and forward list
Node[] selectedNode = em.getSelectedNodes();
if (selectedNode.length > 0) {
Node selectedContext = selectedNode[0];
final String[] selectedPath = NodeOp.createPath(selectedContext, em.getRootContext());
String[] currentLast = backList.peekLast();
String lastNodeName = null;
if (currentLast != null) {
lastNodeName = currentLast[currentLast.length - 1];
}
String selectedNodeName = selectedContext.getName();
if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
//add to the list if the last if not the same as current
backList.addLast(selectedPath); // add the node to the "backList"
if (backList.size() > 1) {
backButton.setEnabled(true);
} else {
backButton.setEnabled(false);
}
forwardList.clear(); // clear the "forwardList"
forwardButton.setEnabled(false); // disable the forward Button
}
updateHistory(em.getSelectedNodes());
}
private void updateHistory(Node[] selectedNodes) {
if (selectedNodes.length == 0) {
return;
}
Node selectedNode = selectedNodes[0];
String selectedNodeName = selectedNode.getName();
/* get the previous entry to make sure we don't duplicate it.
* Motivation for this is also that if we used the back button,
* then we already added the 'current' node to 'back' and we will
* detect that and not reset the forward list.
*/
String[] currentLast = backList.peekLast();
String lastNodeName = null;
if (currentLast != null) {
lastNodeName = currentLast[currentLast.length - 1];
}
if (currentLast == null || !selectedNodeName.equals(lastNodeName)) {
//add to the list if the last if not the same as current
final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
backList.addLast(selectedPath); // add the node to the "backList"
if (backList.size() > 1) {
backButton.setEnabled(true);
} else {
backButton.setEnabled(false);
}
forwardList.clear(); // clear the "forwardList"
forwardButton.setEnabled(false); // disable the forward Button
}
}
/**
* Resets the back and forward list, and also disable the back and forward
* buttons.
*/
private void resetHistory() {
// clear the back and forward list
backList.clear();
forwardList.clear();
backButton.setEnabled(false);
forwardButton.setEnabled(false);
}
@Override
@ -708,17 +728,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
pcs.removePropertyChangeListener(listener);
}
/**
* Resets the back and forward list, and also disable the back and forward
* buttons.
*/
private void resetHistoryListAndButtons() {
// clear the back and forward list
backList.clear();
forwardList.clear();
backButton.setEnabled(false);
forwardButton.setEnabled(false);
}
/**
* Gets the tree on this DirectoryTreeTopComponent.
@ -845,19 +855,17 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
if (path.length > 0 && (rootNodeName == null || path[0].equals(rootNodeName))) {
try {
final TreeView tree = getTree();
Node newSelection = NodeOp.findPath(em.getRootContext(), path);
//resetHistoryListAndButtons();
if (newSelection != null) {
if (rootNodeName != null) {
//called from tree auto refresh context
//remove last from backlist, because auto select will result in duplication
backList.pollLast();
}
//select
//tree.expandNode(newSelection);
em.setExploredContextAndSelection(newSelection, new Node[]{newSelection});
}
// We need to set the selection, which will refresh dataresult and get rid of the oob exception
} catch (NodeNotFoundException ex) {
logger.log(Level.WARNING, "Node not found", ex);

View File

@ -164,7 +164,10 @@ public final class ExtractUnallocAction extends AbstractAction {
private List<LayoutFile> getUnallocFiles(Content c) {
UnallocVisitor uv = new UnallocVisitor();
try {
return c.getChildren().get(0).accept(uv); //Launching it on the root directory
List<Content> unallocFiles = c.getChildren();
if (null != unallocFiles && unallocFiles.isEmpty() == false) {
return unallocFiles.get(0).accept(uv); //Launching it on the root directory
}
} catch (TskCoreException tce) {
logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at sending out the visitor ", tce);
}

View File

@ -16,96 +16,108 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.ingest;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.ingest.IngestDialogPanel;
import static org.sleuthkit.autopsy.ingest.IngestDialogPanel.DISABLED_MOD;
import static org.sleuthkit.autopsy.ingest.IngestDialogPanel.PARSE_UNALLOC;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleAbstract;
import org.sleuthkit.datamodel.Content;
@ServiceProvider(service = IngestConfigurator.class)
public class GeneralIngestConfigurator implements IngestConfigurator {
public class GeneralIngestConfigurator implements IngestConfigurator {
public static final String ENABLED_INGEST_MODULES_KEY = "Enabled_Ingest_Modules";
public static final String PARSE_UNALLOC_SPACE_KEY = "Process_Unallocated_Space";
private List<Content> contentToIngest;
private IngestManager manager;
private IngestDialogPanel ingestDialogPanel;
private String moduleContext;
public GeneralIngestConfigurator() {
this.moduleContext = IngestManager.MODULE_PROPERTIES; // Hard-code this for now.
this.moduleContext = IngestManager.MODULE_PROPERTIES;
ingestDialogPanel = new IngestDialogPanel();
ingestDialogPanel.setContext(moduleContext);
manager = IngestManager.getDefault();
loadSettings();
}
@Override
public void setContext(String context) {
public List<String> setContext(String context) {
moduleContext = context;
ingestDialogPanel.setContext(moduleContext);
reload();
return loadSettingsForContext();
}
private List<String> loadSettingsForContext() {
List<String> messages = new ArrayList<>();
// If there is no enabled ingest modules setting for this user, default to enabling all
// of the ingest modules the IngestManager has loaded.
if (ModuleSettings.settingExists(moduleContext, ENABLED_INGEST_MODULES_KEY) == false) {
String defaultSetting = moduleListToCsv(IngestManager.getDefault().enumerateAllModules());
ModuleSettings.setConfigSetting(moduleContext, ENABLED_INGEST_MODULES_KEY, defaultSetting);
}
// Get the enabled ingest modules setting, check for missing modules, and pass the setting to
// the UI component.
List<IngestModuleAbstract> allModules = IngestManager.getDefault().enumerateAllModules();
String[] enabledModuleNames = ModuleSettings.getConfigSetting(moduleContext, ENABLED_INGEST_MODULES_KEY).split(", ");
List<IngestModuleAbstract> enabledModules = new ArrayList<>();
for (String moduleName : enabledModuleNames) {
IngestModuleAbstract moduleFound = null;
for (IngestModuleAbstract module : allModules) {
if (moduleName.equals(module.getName())) {
moduleFound = module;
break;
}
}
if (moduleFound != null) {
enabledModules.add(moduleFound);
}
else {
messages.add("Unable to load " + moduleName + " module");
}
}
ingestDialogPanel.setEnabledIngestModules(enabledModules);
// If there is no process unallocated space flag setting, default it to false.
if (ModuleSettings.settingExists(moduleContext, PARSE_UNALLOC_SPACE_KEY) == false) {
ModuleSettings.setConfigSetting(moduleContext, PARSE_UNALLOC_SPACE_KEY, "false");
}
// Get the process unallocated space flag setting and pass it to the UI component.
boolean processUnalloc = Boolean.parseBoolean(ModuleSettings.getConfigSetting(moduleContext, PARSE_UNALLOC_SPACE_KEY));
ingestDialogPanel.setProcessUnallocSpaceEnabled(processUnalloc);
return messages;
}
@Override
public JPanel getIngestConfigPanel() {
// Note that this panel allows for selecting modules for the ingest process,
// specifying the process unallocated space flag, and also specifying settings
// for a selected ingest module.
return ingestDialogPanel;
}
@Override
public JPanel getIngestConfigPanel() {
return ingestDialogPanel;
}
@Override
public void setContent(List<Content> inputContent) {
this.contentToIngest = inputContent;
}
@Override
public void start() {
// Get the list of ingest modules selected by the user.
List<IngestModuleAbstract> modulesToStart = ingestDialogPanel.getModulesToStart();
public void save() {
// Save the user's configuration of the set of enabled ingest modules.
String enabledModulesCsvList = moduleListToCsv(ingestDialogPanel.getModulesToStart());
ModuleSettings.setConfigSetting(moduleContext, ENABLED_INGEST_MODULES_KEY, enabledModulesCsvList);
// Get the user's selection of whether or not to process unallocated space.
manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled());
// Start the ingest.
if (!modulesToStart.isEmpty()) {
manager.execute(modulesToStart, contentToIngest);
}
}
@Override
public void save() {
// Save the user's configuration of the currently selected module.
// Save the user's setting for the process unallocated space flag.
String processUnalloc = Boolean.toString(ingestDialogPanel.processUnallocSpaceEnabled());
ModuleSettings.setConfigSetting(moduleContext, PARSE_UNALLOC_SPACE_KEY, processUnalloc);
// Save the user's configuration of the currently selected ingest module.
IngestModuleAbstract currentModule = ingestDialogPanel.getCurrentIngestModule();
if (currentModule != null && currentModule.hasSimpleConfiguration()) {
currentModule.saveSimpleConfiguration();
}
// Create a list of the modules the user wants to be disabled.
List<IngestModuleAbstract> disabledModules = IngestManager.getDefault().enumerateAllModules();
disabledModules.removeAll(ingestDialogPanel.getModulesToStart());
String disabledModulesCsv = moduleListToCsv(disabledModules);
// Save the user's general ingest configuration.
ModuleSettings.setConfigSetting(moduleContext, DISABLED_MOD, disabledModulesCsv);
String processUnalloc = Boolean.toString(ingestDialogPanel.processUnallocSpaceEnabled());
ModuleSettings.setConfigSetting(moduleContext, PARSE_UNALLOC, processUnalloc);
}
}
@Override
public void reload() {
loadSettings();
}
@Override
public boolean isIngestRunning() {
return manager.isIngestRunning();
}
private static String moduleListToCsv(List<IngestModuleAbstract> lst) {
if (lst == null || lst.isEmpty()) {
return "";
@ -121,39 +133,28 @@ public class GeneralIngestConfigurator implements IngestConfigurator {
return sb.toString();
}
private static List<IngestModuleAbstract> csvToModuleList(String csv) {
List<IngestModuleAbstract> modules = new ArrayList<>();
if (csv == null || csv.isEmpty()) {
return modules;
}
String[] moduleNames = csv.split(", ");
List<IngestModuleAbstract> allModules = IngestManager.getDefault().enumerateAllModules();
for (String moduleName : moduleNames) {
for (IngestModuleAbstract module : allModules) {
if (moduleName.equals(module.getName())) {
modules.add(module);
break;
}
}
}
return modules;
@Override
public void setContent(List<Content> inputContent) {
this.contentToIngest = inputContent;
}
private void loadSettings() {
// get the csv list of disabled modules
String disabledModulesCsv = ModuleSettings.getConfigSetting(moduleContext, DISABLED_MOD);
@Override
public void start() {
// Get the list of ingest modules selected by the user.
List<IngestModuleAbstract> modulesToStart = ingestDialogPanel.getModulesToStart();
// create a list of modules from it
List<IngestModuleAbstract> disabledModules = csvToModuleList(disabledModulesCsv);
// tell the ingestDialogPanel to unselect these modules
ingestDialogPanel.setDisabledModules(disabledModules);
boolean processUnalloc = Boolean.parseBoolean(ModuleSettings.getConfigSetting(moduleContext, PARSE_UNALLOC));
ingestDialogPanel.setProcessUnallocSpaceEnabled(processUnalloc);
// Get the user's selection of whether or not to process unallocated space.
manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled());
if (!modulesToStart.isEmpty() && contentToIngest != null) {
// Queue the ingest process.
manager.execute(modulesToStart, contentToIngest);
}
}
@Override
public boolean isIngestRunning() {
return manager.isIngestRunning();
}
}

View File

@ -0,0 +1,84 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2013 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.ingest;
import java.util.List;
import javax.swing.JPanel;
import org.sleuthkit.datamodel.Content;
/**
* Instances of this class provide the following services:
* 1. A way to save and load the ingest process configuration settings for a
* given ingest process context.
* 2. A UI component for configuring ingest process settings.
* 3. A way to specify input content and start the ingest process for a
* given ingest process context.
*/
// @@@ This interface needs to be re-designed. An interface for allowing the
// authors of ingest modules to expose context sensitive module configuration
// settings needs to be provided; there also needs to be a way for users to
// configure the ingest process that uses those modules. These are separate
// concerns; likewise, kicking off an ingest process for particular content in
// a particular context is a separate concern.
public interface IngestConfigurator {
/**
* Specifies the ingest process context for the purpose of choosing, saving,
* and loading ingest process configuration settings; also determines what
* configuration settings will be in effect if the setContent() and start()
* methods are called to start the ingest process for some content specified
* using the setContent() method.
* @return A list, possibly empty, of messages describing errors that
* occurred when loading the configuration settings.
*/
public List<String> setContext(String contextName);
/**
* Provides a UI component for choosing ingest process configuration
* settings for the ingest process context specified using the setContext()
* method.
*/
JPanel getIngestConfigPanel();
/**
* Saves the ingest process configuration settings for the ingest process
* context specified using the setContext() method.
*/
void save();
/**
* Sets the input content for an ingest process prior to calling start() to
* run the process using the process configuration settings for the context
* specified using setContext().
*/
void setContent(List<Content> inputContent);
/**
* Starts (queues) the ingest process for the content specified using the
* setContent() method, using the configuration settings corresponding to
* the ingest process context specified using the setContext() method.
*/
void start();
/**
* Returns true if any ingest process is running, false otherwise.
* Note that the running process may or may not be the process started
* (queued) by an invocation of the start() method.
*/
boolean isIngestRunning();
}

View File

@ -30,11 +30,9 @@ import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.GeneralIngestConfigurator;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.casemodule.IngestConfigurator;
/**
* Dialog box that allows ingest modules to be run on an image.
@ -49,8 +47,14 @@ public class IngestDialog extends JDialog {
public IngestDialog(JFrame frame, String title, boolean modal) {
super(frame, title, modal);
ingestConfigurator = new GeneralIngestConfigurator();
ingestConfigurator.setContext(IngestDialog.class.getCanonicalName());
ingestConfigurator.reload();
List<String> messages = ingestConfigurator.setContext(IngestDialog.class.getCanonicalName());
if (messages.isEmpty() == false) {
StringBuilder warning = new StringBuilder();
for (String message : messages) {
warning.append(message).append("\n");
}
JOptionPane.showMessageDialog(null, warning.toString());
}
}
public IngestDialog(){

View File

@ -44,8 +44,6 @@ public class IngestDialogPanel extends javax.swing.JPanel {
private IngestModuleAbstract currentModule;
private ModulesTableModel tableModel;
public static final String DISABLED_MOD = "Disabled_Ingest_Modules";
public static final String PARSE_UNALLOC = "Process_Unallocated_Space";
private String context;
/**
@ -123,8 +121,8 @@ public class IngestDialogPanel extends javax.swing.JPanel {
processUnallocCheckbox.setSelected(enabled);
}
public void setDisabledModules(List<IngestModuleAbstract> disabledModules) {
tableModel.setUnselectedModules(disabledModules);
public void setEnabledIngestModules(List<IngestModuleAbstract> enabledModules) {
tableModel.setSelectedModules(enabledModules);
}
/**
@ -407,7 +405,7 @@ public class IngestDialogPanel extends javax.swing.JPanel {
}
/**
* Custom cell renderer for tooltips with module description
* Custom cell renderer for tool tips with module description
*/
private class ModulesTableRenderer extends DefaultTableCellRenderer {

View File

@ -475,6 +475,9 @@ public final class IngestModuleLoader {
for (final ModuleInfo moduleInfo : moduleInfos) {
if (moduleInfo.isEnabled()) {
/* NOTE: We have an assumption here that the modules in an NBM will
* have the same package name as the NBM name. This means that
* an NBM can have only one package with modules in it. */
String basePackageName = moduleInfo.getCodeNameBase();
// skip the standard ones

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.hashdatabase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -375,4 +376,14 @@ public class HashDbIngestModule extends IngestModuleAbstractFile {
return ret;
}
public ArrayList<String> getKnownBadSetNames() {
ArrayList<String> knownBadSetNames = new ArrayList<>();
HashDbXML hdbxml = HashDbXML.getCurrent();
for (HashDb db : hdbxml.getKnownBadSets()) {
knownBadSetNames.add(db.getName());
}
return knownBadSetNames;
}
}

View File

@ -59,8 +59,10 @@ public class AbstractFileHtmlExtract implements AbstractFileExtract {
"text/javascript" //"application/xml",
//"application/xml-dtd",
);
private final TikaLanguageIdentifier tikaLanguageIdentifier;
AbstractFileHtmlExtract() {
tikaLanguageIdentifier = new TikaLanguageIdentifier();
this.module = KeywordSearchIngestModule.getDefault();
ingester = Server.getIngester();
}
@ -148,6 +150,7 @@ public class AbstractFileHtmlExtract implements AbstractFileExtract {
//logger.log(Level.INFO, "TOTAL READ SIZE: " + totalRead + " file: " + sourceFile.getName());
//encode to bytes to index as byte stream
String extracted;
//add BOM and trim the 0 bytes
//set initial size to chars read + bom - try to prevent from resizing
StringBuilder sb = new StringBuilder((int) totalRead + 1000);
@ -163,6 +166,11 @@ public class AbstractFileHtmlExtract implements AbstractFileExtract {
totalRead = 0;
extracted = sb.toString();
//attempt to identify language of extracted text and post it to the blackboard
tikaLanguageIdentifier.addLanguageToBlackBoard(extracted, sourceFile);
//converts BOM automatically to charSet encoding
byte[] encodedBytes = extracted.getBytes(outCharset);
AbstractFileChunk chunk = new AbstractFileChunk(this, this.numChunks + 1);
@ -216,13 +224,11 @@ public class AbstractFileHtmlExtract implements AbstractFileExtract {
public boolean isSupported(AbstractFile file, String detectedFormat) {
if (detectedFormat == null) {
return false;
}
else if (WEB_MIME_TYPES.contains(detectedFormat) ) {
} else if (WEB_MIME_TYPES.contains(detectedFormat)) {
return true;
}
else {
} else {
return false;
}
}
}

View File

@ -39,11 +39,16 @@ import org.sleuthkit.autopsy.ingest.IngestModuleAbstractFile;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.apache.tika.Tika;
import org.apache.tika.language.LanguageIdentifier;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.ParseContext;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.StringExtract;
import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Extractor of text from TIKA supported AbstractFile content. Extracted text is
@ -71,8 +76,10 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
//private static final String UTF16BOM = "\uFEFF"; disabled prepending of BOM
private final ExecutorService tikaParseExecutor = Executors.newSingleThreadExecutor();
private final List<String> TIKA_SUPPORTED_TYPES = new ArrayList<String>();
private final TikaLanguageIdentifier tikaLanguageIdentifier;
AbstractFileTikaTextExtract() {
tikaLanguageIdentifier = new TikaLanguageIdentifier();
this.module = KeywordSearchIngestModule.getDefault();
ingester = Server.getIngester();
@ -123,7 +130,7 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
final InputStream stream = new ReadContentInputStream(sourceFile);
try {
Metadata meta = new Metadata();
//Parse the file in a task
Tika tika = new Tika(); //new tika instance for every file, to workaround tika memory issues
ParseRequestTask parseTask = new ParseRequestTask(tika, stream, meta, sourceFile);
@ -152,7 +159,7 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
return false;
}
// break the results into chunks and index
success = true;
long readSize;
@ -213,7 +220,10 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
}
extracted = sb.toString();
//attempt to identify language of extracted text and post it to the blackboard
tikaLanguageIdentifier.addLanguageToBlackBoard(extracted, sourceFile);
//converts BOM automatically to charSet encoding
byte[] encodedBytes = extracted.getBytes(OUTPUT_CHARSET);
AbstractFileChunk chunk = new AbstractFileChunk(this, this.numChunks + 1);
@ -272,7 +282,7 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
if (detectedFormat == null) {
return false;
} else if (detectedFormat.equals("application/octet-stream")
|| detectedFormat.equals("application/x-msdownload") ) {
|| detectedFormat.equals("application/x-msdownload")) {
//any binary unstructured blobs (string extraction will be used)
return false;
} else if (AbstractFileExtract.ARCHIVE_MIME_TYPES.contains(detectedFormat)) {
@ -292,8 +302,8 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract {
}
/**
* Runnable task that calls tika to parse the content using
* the input stream. Provides reader for results.
* Runnable task that calls tika to parse the content using the input
* stream. Provides reader for results.
*/
private static class ParseRequestTask implements Runnable {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,9 +21,6 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.logging.Level;
import org.apache.solr.client.solrj.SolrServerException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentation;
@ -35,7 +32,6 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentatio
abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel implements KeywordSearchPerformerInterface {
protected int filesIndexed;
private static final Logger logger = Logger.getLogger(AbstractKeywordSearchPerformer.class.getName());
AbstractKeywordSearchPerformer() {
initListeners();
@ -47,7 +43,6 @@ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel impleme
@Override
public void propertyChange(PropertyChangeEvent evt) {
String changed = evt.getPropertyName();
Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue();
if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) {
@ -114,7 +109,7 @@ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel impleme
KeywordSearchUtil.displayDialog("Keyword Search Error", "Keyword list is empty, please add at least one keyword to the list", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
return;
}
man = new KeywordSearchQueryManager(keywords, Presentation.COLLAPSE);
man = new KeywordSearchQueryManager(keywords, Presentation.FLAT);
}
else {
QueryType queryType = null;
@ -128,7 +123,7 @@ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel impleme
KeywordSearchUtil.displayDialog("Keyword Search Error", "Please enter a keyword to search for", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
return;
}
man = new KeywordSearchQueryManager(getQueryText(), queryType, Presentation.COLLAPSE);
man = new KeywordSearchQueryManager(getQueryText(), queryType, Presentation.FLAT);
}
if (man.validate()) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -28,7 +28,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.windows.TopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.datamodel.KeyValue;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;
@ -41,7 +40,9 @@ public class KeywordSearchQueryManager {
// how to display the results
public enum Presentation {
COLLAPSE, DETAIL
FLAT, // all results are in a single level (even if multiple keywords and reg-exps are used). We made this because we were having problems with multiple-levels of nodes and the thumbnail and table view sharing an ExplorerManager. IconView seemed to change EM so that it did not allow lower levels to be selected.
COLLAPSE, // two levels. Keywords on top, files on bottom.
DETAIL // not currently used, but seems like it has three levels of nodes
};
private List<Keyword> keywords;
@ -70,7 +71,7 @@ public class KeywordSearchQueryManager {
* @param presentation Presentation Layout
*/
public KeywordSearchQueryManager(String query, QueryType qt, Presentation presentation) {
keywords = new ArrayList<Keyword>();
keywords = new ArrayList<>();
keywords.add(new Keyword(query, qt == QueryType.REGEX ? false : true));
this.presentation = presentation;
queryType = qt;
@ -84,7 +85,7 @@ public class KeywordSearchQueryManager {
* @param presentation Presentation layout
*/
public KeywordSearchQueryManager(String query, boolean isLiteral, Presentation presentation) {
keywords = new ArrayList<Keyword>();
keywords = new ArrayList<>();
keywords.add(new Keyword(query, isLiteral));
this.presentation = presentation;
queryType = isLiteral ? QueryType.WORD : QueryType.REGEX;
@ -96,7 +97,7 @@ public class KeywordSearchQueryManager {
* Create a list of queries to later run
*/
private void init() {
queryDelegates = new ArrayList<KeywordSearchQuery>();
queryDelegates = new ArrayList<>();
for (Keyword keyword : keywords) {
KeywordSearchQuery query = null;
switch (queryType) {
@ -137,18 +138,17 @@ public class KeywordSearchQueryManager {
// } else {
//Collapsed view
Collection<KeyValueQuery> things = new ArrayList<KeyValueQuery>();
Collection<KeyValueQuery> things = new ArrayList<>();
int queryID = 0;
StringBuilder queryConcat = new StringBuilder(); // concatenation of all query strings
for (KeywordSearchQuery q : queryDelegates) {
Map<String, Object> kvs = new LinkedHashMap<String, Object>();
Map<String, Object> kvs = new LinkedHashMap<>();
final String queryStr = q.getQueryString();
queryConcat.append(queryStr).append(" ");
things.add(new KeyValueQuery(queryStr, kvs, ++queryID, q));
}
Node rootNode = null;
Node rootNode;
String queryConcatStr = queryConcat.toString();
final int queryConcatStrLen = queryConcatStr.length();
final String queryStrShort = queryConcatStrLen > 15 ? queryConcatStr.substring(0, 14) + "..." : queryConcatStr;
@ -156,7 +156,7 @@ public class KeywordSearchQueryManager {
DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance(windowTitle);
if (things.size() > 0) {
Children childThingNodes =
Children.create(new KeywordSearchResultFactory(keywords, things, Presentation.COLLAPSE, searchResultWin), true);
Children.create(new KeywordSearchResultFactory(keywords, things, presentation, searchResultWin), true);
rootNode = new AbstractNode(childThingNodes);
} else {
@ -179,7 +179,7 @@ public class KeywordSearchQueryManager {
boolean allValid = true;
for (KeywordSearchQuery tcq : queryDelegates) {
if (!tcq.validate()) {
logger.log(Level.WARNING, "Query has invalid syntax: " + tcq.getQueryString());
logger.log(Level.WARNING, "Query has invalid syntax: {0}", tcq.getQueryString());
allValid = false;
break;
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -36,7 +36,6 @@ import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Cancellable;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
@ -109,7 +108,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
}
KeywordSearchResultFactory(Keyword query, Collection<KeyValueQuery> things, Presentation presentation, DataResultTopComponent viewer) {
queries = new ArrayList<Keyword>();
queries = new ArrayList<>();
queries.add(query);
this.presentation = presentation;
this.things = things;
@ -134,7 +133,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
for (int i = 0; i < FS_PROPS_LEN; ++i) {
toSet.put(fsTypes[i].toString(), "");
}
}
public static void setCommonProperty(Map<String, Object> toSet, CommonPropertyTypes type, String value) {
@ -150,10 +148,21 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
@Override
protected boolean createKeys(List<KeyValueQuery> toPopulate) {
int id = 0;
if (presentation == Presentation.DETAIL) {
if (presentation == Presentation.FLAT) {
for (KeyValueQuery thing : things) {
Map<String, Object> map = thing.getMap();
initCommonProperties(map);
final String query = thing.getName();
setCommonProperty(map, CommonPropertyTypes.KEYWORD, query);
setCommonProperty(map, CommonPropertyTypes.REGEX, Boolean.valueOf(!thing.getQuery().isEscaped()));
ResultCollapsedChildFactory childFactory = new ResultCollapsedChildFactory(thing);
childFactory.createKeysForFlatNodes(toPopulate);
}
}
else if (presentation == Presentation.DETAIL) {
Iterator<KeyValueQuery> it = things.iterator();
for (Keyword keyword : queries) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
Map<String, Object> map = new LinkedHashMap<>();
final String query = keyword.getQuery();
initCommonProperties(map);
setCommonProperty(map, CommonPropertyTypes.KEYWORD, query);
@ -166,25 +175,26 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
}
} else {
for (KeyValueQuery thing : things) {
//Map<String, Object> map = new LinkedHashMap<String, Object>();
Map<String, Object> map = thing.getMap();
initCommonProperties(map);
final String query = thing.getName();
setCommonProperty(map, CommonPropertyTypes.KEYWORD, query);
setCommonProperty(map, CommonPropertyTypes.REGEX, Boolean.valueOf(!thing.getQuery().isEscaped()));
//toPopulate.add(new KeyValue(query, map, ++id));
toPopulate.add(thing);
}
}
return true;
}
@Override
protected Node createNodeForKey(KeyValueQuery thing) {
ChildFactory<KeyValueQuery> childFactory = null;
if (presentation == Presentation.COLLAPSE) {
ChildFactory<KeyValueQuery> childFactory;
if (presentation == Presentation.FLAT) {
ResultCollapsedChildFactory factory = new ResultCollapsedChildFactory(thing);
return factory.createFlatNodeForKey(thing);
}
else if (presentation == Presentation.COLLAPSE) {
childFactory = new ResultCollapsedChildFactory(thing);
final Node ret = new KeyValueNode(thing, Children.create(childFactory, true));
SwingUtilities.invokeLater(new Runnable() {
@ -199,7 +209,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
});
return ret;
} else {
childFactory = new ResulTermsMatchesChildFactory(things);
return new KeyValueNode(thing, Children.create(childFactory, true));
}
@ -218,9 +227,13 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
this.queryThing = queryThing;
}
// @@@ This method is a workaround until we decide whether we need all three presentation modes or FLAT is sufficient.
public boolean createKeysForFlatNodes(List<KeyValueQuery> toPopulate) {
return createKeys(toPopulate);
}
@Override
protected boolean createKeys(List<KeyValueQuery> toPopulate) {
//final String origQuery = queryThing.getName();
final KeyValueQuery queryThingQuery = queryThing;
final KeywordSearchQuery tcq = queryThingQuery.getQuery();
@ -252,7 +265,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
for (final AbstractFile f : hitContents.keySet()) {
final int previewChunk = hitContents.get(f);
//get unique match result files
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
Map<String, Object> resMap = new LinkedHashMap<>();
setCommonProperty(resMap, CommonPropertyTypes.MATCH, f.getName());
try {
@ -306,12 +319,11 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
//whereas in bb we write every hit per file separately
new ResultWriter(tcqRes, tcq, listName).execute();
return true;
}
private String getHighlightQuery(KeywordSearchQuery tcq, boolean literal_query, Map<String, List<ContentHit>> tcqRes, AbstractFile f) {
String highlightQueryEscaped = null;
String highlightQueryEscaped;
if (literal_query) {
//literal, treat as non-regex, non-term component query
highlightQueryEscaped = tcq.getQueryString();
@ -326,7 +338,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
highlightQuery.append(term);
} else {
//find terms for this file hit
List<String> hitTerms = new ArrayList<String>();
List<String> hitTerms = new ArrayList<>();
for (String term : tcqRes.keySet()) {
List<ContentHit> hitList = tcqRes.get(term);
@ -363,10 +375,13 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
return highlightQueryEscaped;
}
// @@@ This method is a workaround until we decide whether we need all three presentation modes or FLAT is sufficient.
public Node createFlatNodeForKey(KeyValueQuery thing) {
return createNodeForKey(thing);
}
@Override
protected Node createNodeForKey(KeyValueQuery thing) {
//return new KeyValueNode(thing, Children.LEAF);
//return new KeyValueNode(thing, Children.create(new ResultFilesChildFactory(thing), true));
final KeyValueQueryContent thingContent = (KeyValueQueryContent) thing;
final Content content = thingContent.getContent();
final String queryStr = thingContent.getQueryStr();
@ -377,7 +392,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, queryStr, !thingContent.getQuery().isEscaped(), false, hits);
return new KeywordSearchFilterNode(highlights, kvNode, queryStr, previewChunk);
}
}
@ -438,12 +452,11 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
for (final AbstractFile f : uniqueMatches.keySet()) {
final int previewChunkId = uniqueMatches.get(f);
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
Map<String, Object> resMap = new LinkedHashMap<>();
if (f.getType() == TSK_DB_FILES_TYPE_ENUM.FS) {
AbstractFsContentNode.fillPropertyMap(resMap, (FsContent) f);
}
toPopulate.add(new KeyValueQueryContent(f.getName(), resMap, ++resID, f, keywordQuery, thing.getQuery(), previewChunkId, matchesRes));
}
//write to bb
new ResultWriter(matchesRes, origQuery, "").execute();
@ -459,7 +472,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
final int previewChunk = thingContent.getPreviewChunk();
final Map<String, List<ContentHit>> hits = thingContent.getHits();
Node kvNode = new KeyValueNode(thingContent, Children.LEAF, Lookups.singleton(content));
//wrap in KeywordSearchFilterNode for the markup content
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query, !thingContent.getQuery().isEscaped(), hits);
@ -475,7 +487,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
private Content content;
private String queryStr;
private KeywordSearchQuery query;
private int previewChunk;
private Map<String, List<ContentHit>> hits;
@ -510,7 +521,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
*/
static class ResultWriter extends SwingWorker<Object, Void> {
private static List<ResultWriter> writers = new ArrayList<ResultWriter>();
private static List<ResultWriter> writers = new ArrayList<>();
//lock utilized to enqueue writers and limit execution to 1 at a time
private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy
//private static final Lock writerLock = rwLock.writeLock();
@ -518,14 +529,13 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
private KeywordSearchQuery query;
private String listName;
private Map<String, List<ContentHit>> hits;
final Collection<BlackboardArtifact> na = new ArrayList<BlackboardArtifact>();
final Collection<BlackboardArtifact> na = new ArrayList<>();
private static final int QUERY_DISPLAY_LEN = 40;
ResultWriter(Map<String, List<ContentHit>> hits, KeywordSearchQuery query, String listName) {
this.hits = hits;
this.query = query;
this.listName = listName;
}
protected void finalizeWorker() {
@ -539,7 +549,6 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
}
});
if (!this.isCancelled() && !na.isEmpty()) {
IngestServices.getDefault().fireModuleDataEvent(new ModuleDataEvent(KeywordSearchIngestModule.MODULE_NAME, ARTIFACT_TYPE.TSK_KEYWORD_HIT, na));
}
@ -573,7 +582,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
for (AbstractFile f : flattened.keySet()) {
int chunkId = flattened.get(f);
final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(hit);
String snippet = null;
String snippet;
try {
snippet = LuceneQuery.querySnippet(snippetQuery, f.getId(), chunkId, !query.isLiteral(), true);
} catch (NoOpenCoreException e) {
@ -591,15 +600,11 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
}
}
}
}
} finally {
//writerLock.unlock();
finalizeWorker();
}
return null;
}

View File

@ -0,0 +1,39 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 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.keywordsearch;
import org.sleuthkit.datamodel.AbstractFile;
/**
*
*
*/
public interface TextLanguageIdentifier {
/**
* attempts to identify the language of the given String and add it to the
* black board for the given {@code AbstractFile} as a TSK_TEXT_LANGUAGE
* attribute on a TSK_GEN_INFO artifact.
*
* @param extracted the String whose language is to be identified
* @param sourceFile the AbstractFile the string is extracted from.
* @return
*/
public void addLanguageToBlackBoard(String extracted, AbstractFile sourceFile);
}

View File

@ -0,0 +1,63 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 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.keywordsearch;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* TextLanguageIdentifier implementation based on a wrapped Tike
* LanguageIdentifier
*/
public class TikaLanguageIdentifier implements TextLanguageIdentifier {
private static final Logger logger = Logger.getLogger(TikaLanguageIdentifier.class.getName());
private static final int MIN_STRING_LENGTH = 1000;
@Override
public void addLanguageToBlackBoard(String extracted, AbstractFile sourceFile) {
if (extracted.length() > MIN_STRING_LENGTH) {
org.apache.tika.language.LanguageIdentifier li = new org.apache.tika.language.LanguageIdentifier(extracted);
logger.log(Level.INFO, sourceFile.getName() + " detected language: " + li.getLanguage()
+ " with " + ((li.isReasonablyCertain()) ? "HIGH" : "LOW") + " confidence");
BlackboardArtifact genInfo;
try {
genInfo = sourceFile.getGenInfoArtifact();
BlackboardAttribute textLang = new BlackboardAttribute(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT_LANGUAGE.getTypeID(),
KeywordSearchIngestModule.MODULE_NAME, li.getLanguage());
genInfo.addAttribute(textLang);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "failed to add TSK_TEXT_LANGUAGE attribute to TSK_GEN_INFO artifact for file: " + sourceFile.getName(), ex);
}
} else {
logger.info("extracted text too short, skipping language detection on " + sourceFile.getName());
}
}
}

View File

@ -138,7 +138,7 @@ public class Chrome extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? result.get("url").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//TODO Revisit usage of deprecated constructor per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", ((Long.valueOf(result.get("last_visit_time").toString())) / 10000000)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "Recent Activity", ((Long.valueOf(result.get("last_visit_time").toString())) / 10000000)));
@ -232,7 +232,7 @@ public class Chrome extends Extract {
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", (date / 10000000)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "Recent Activity", (date / 10000000)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "Recent Activity", url));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", EscapeUtil.decodeURL(url)));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", EscapeUtil.decodeURL(url)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "Recent Activity", name));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), "Recent Activity", "Chrome"));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), "Recent Activity", domain));
@ -299,7 +299,7 @@ public class Chrome extends Extract {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "Recent Activity", ((result.get("value").toString() != null) ? result.get("value").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), "Recent Activity", "Chrome"));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "Recent Activity", ((result.get("host_key").toString() != null) ? result.get("host_key").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("host_key").toString() != null) ? EscapeUtil.decodeURL(result.get("host_key").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("host_key").toString() != null) ? EscapeUtil.decodeURL(result.get("host_key").toString()) : "")));
String domain = result.get("host_key").toString();
domain = domain.replaceFirst("^\\.+(?!$)", "");
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), "Recent Activity", domain));
@ -358,7 +358,7 @@ public class Chrome extends Extract {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH.getTypeID(), "Recent Activity", (result.get("full_path").toString())));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID(), "Recent Activity", Util.findID(dataSource, (result.get("full_path").toString()))));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? result.get("url").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
Long time = (Long.valueOf(result.get("start_time").toString()));
String Tempdate = time.toString();
time = Long.valueOf(Tempdate) / 10000000;
@ -417,7 +417,7 @@ public class Chrome extends Extract {
for (HashMap<String, Object> result : tempList) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "Recent Activity", ((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("origin_url").toString() != null) ? EscapeUtil.decodeURL(result.get("origin_url").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("origin_url").toString() != null) ? EscapeUtil.decodeURL(result.get("origin_url").toString()) : "")));
//TODO Revisit usage of deprecated constructor as per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", ((Long.valueOf(result.get("last_visit_time").toString())) / 1000000)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "Recent Activity", ((Long.valueOf(result.get("last_visit_time").toString())) / 1000000)));

View File

@ -136,7 +136,7 @@ public class ExtractIE extends Extract {
}
try {
this.parsePascoResults(pascoResults);
this.getHistory(pascoResults);
}
catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing IE History", e);
@ -189,7 +189,7 @@ public class ExtractIE extends Extract {
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", "Last Visited", datetime));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "RecentActivity", datetime));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", url));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(url)));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(url)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", name));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), "RecentActivity", "Internet Explorer"));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), "RecentActivity", domain));
@ -238,7 +238,7 @@ public class ExtractIE extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", url));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(url)));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(url)));
//TODO Revisit usage of deprecated Constructor as of TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), "RecentActivity", "Last Visited", datetime));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), "RecentActivity", datetime));
@ -394,7 +394,7 @@ public class ExtractIE extends Extract {
execPasco.execute(writer, JAVA_PATH,
"-cp", PASCO_LIB_PATH,
"isi.pasco2.Main", "-T", "history", indexFilePath );
// @@@ Investigate use of history versus cache as type.
} catch (IOException ex) {
success = false;
logger.log(Level.SEVERE, "Unable to execute Pasco to process Internet Explorer web history.", ex);
@ -416,7 +416,7 @@ public class ExtractIE extends Extract {
return success;
}
private void parsePascoResults(List<String> filenames) {
private void getHistory(List<String> filenames) {
if (pascoFound == false) {
return;
}
@ -451,6 +451,15 @@ public class ExtractIE extends Extract {
String line = fileScanner.nextLine();
// lines at end of file
if ((line.startsWith("LEAK entries")) ||
(line.startsWith("REDR entries")) ||
(line.startsWith("URL entries")) ||
(line.startsWith("ent entries")) ||
(line.startsWith("unknown entries"))) {
continue;
}
if (line.startsWith("URL")) {
String[] lineBuff = line.split("\\t");
@ -466,6 +475,10 @@ public class ExtractIE extends Extract {
String realurl = "";
String domain = "";
/* We've seen two types of lines:
* URL http://XYZ.com ....
* URL Visited: Joe@http://XYZ.com ....
*/
if (lineBuff[1].contains("@")) {
String url[] = lineBuff[1].split("@", 2);
user = url[0];
@ -505,7 +518,7 @@ public class ExtractIE extends Extract {
BlackboardArtifact bbart = tskCase.getContentById(artObjId).newArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY);
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", realurl));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(realurl)));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", EscapeUtil.decodeURL(realurl)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "RecentActivity", ftime));

View File

@ -119,7 +119,7 @@ public class Firefox extends Extract {
for (HashMap<String, Object> result : tempList) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? result.get("url").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//TODO Revisit usage of deprecated constructor as per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", "Last Visited", (Long.valueOf(result.get("visit_date").toString()))));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "RecentActivity", (Long.valueOf(result.get("visit_date").toString()))));
@ -177,7 +177,7 @@ public class Firefox extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? result.get("url").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", ((result.get("title").toString() != null) ? result.get("title").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), "RecentActivity", "FireFox"));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), "RecentActivity", (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""))));
@ -241,7 +241,7 @@ public class Firefox extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", ((result.get("host").toString() != null) ? result.get("host").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("host").toString() != null) ? EscapeUtil.decodeURL(result.get("host").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("host").toString() != null) ? EscapeUtil.decodeURL(result.get("host").toString()) : "")));
//TODO Revisit usage of deprecated constructor as per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", "Title", ((result.get("name").toString() != null) ? result.get("name").toString() : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), "RecentActivity", "Last Visited", (Long.valueOf(result.get("lastAccessed").toString()))));
@ -312,7 +312,7 @@ public class Firefox extends Extract {
Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
String urldecodedtarget = URLDecoder.decode(result.get("source").toString().replaceAll("file:///", ""), "UTF-8");
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? result.get("source").toString() : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
//TODO Revisit usage of deprecated constructor as per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", "Last Visited", (Long.valueOf(result.get("startTime").toString()))));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "RecentActivity", (Long.valueOf(result.get("startTime").toString()))));

View File

@ -5,4 +5,29 @@
<project name="org.sleuthkit.autopsy.timeline" default="netbeans" basedir=".">
<description>Builds, tests, and runs the project org.sleuthkit.autopsy.timeline.</description>
<import file="nbproject/build-impl.xml"/>
<condition property="os.family.unix">
<os family="unix"/>
</condition>
<!-- Verify that the TSK_HOME env variable is set -->
<target name="findTSK">
<property environment="env"/>
<condition property="tskFound">
<isset property="env.TSK_HOME"/>
</condition>
<fail unless="tskFound" message="TSK_HOME must be set as an environment variable."/>
<echo> TSK_HOME: ${env.TSK_HOME}</echo>
</target>
<target name="getMacTime" depends="findTSK" if="os.family.unix">
<property environment="env"/>
<copy file="${env.TSK_HOME}/tools/timeline/mactime" tofile="${basedir}/release/mactime/mactime.pl"/>
</target>
<target name="init" depends="basic-init,files-init,build-init,-javac-init">
<!-- get additional deps -->
<!-- This should only be done on non-Windows systems. <antcall target="getMacTime" /> -->
</target>
</project>

View File

@ -1,938 +0,0 @@
#!/usr/bin/perl -w
my $VER="4.1.0";
#
# This program is based on the 'mactime' program by Dan Farmer and
# and the 'mac_daddy' program by Rob Lee.
#
# It takes as input data from either 'ils -m' or 'fls -m' (from The Sleuth
# Kit) or 'mac-robber'.
# Based on the dates as arguments given, the data is sorted by and
# printed.
#
# The Sleuth Kit
# Brian Carrier [carrier <at> sleuthkit [dot] org]
# Copyright (c) 2003-2012 Brian Carrier. All rights reserved
#
# TASK
# Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved
#
#
# The modifications to the original mactime are distributed under
# the Common Public License 1.0
#
#
# Copyright 1999 by Dan Farmer. All rights reserved. Some individual
# files may be covered by other copyrights (this will be noted in the
# file itself.)
#
# Redistribution and use in source and binary forms are permitted
# provided that this entire copyright notice is duplicated in all such
# copies.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
use POSIX;
use strict;
my $debug = 0;
# %month_to_digit = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6,
# "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12);
my %digit_to_month = (
"01", "Jan", "02", "Feb", "03", "Mar", "04", "Apr",
"05", "May", "06", "Jun", "07", "Jul", "08", "Aug",
"09", "Sep", "10", "Oct", "11", "Nov", "12", "Dec"
);
my %digit_to_day = (
"0", "Sun", "1", "Mon", "2", "Tue", "3", "Wed",
"4", "Thu", "5", "Fri", "6", "Sat"
);
sub usage {
print <<EOF;
mactime [-b body_file] [-p password_file] [-g group_file] [-i day|hour idx_file] [-d] [-h] [-V] [-y] [-z TIME_ZONE] [DATE]
-b: Specifies the body file location, else STDIN is used
-d: Output in comma delimited format
-h: Display a header with session information
-i [day | hour] file: Specifies the index file with a summary of results
-y: Dates are displayed in ISO 8601 format
-m: Dates have month as number instead of word (does not work with -y)
-z: Specify the timezone the data came from (in the local system format) (does not work with -y)
-g: Specifies the group file location, else GIDs are used
-p: Specifies the password file location, else UIDs are used
-V: Prints the version to STDOUT
[DATE]: starting date (yyyy-mm-dd) or range (yyyy-mm-dd..yyyy-mm-dd)
EOF
exit(1);
}
sub version {
print "The Sleuth Kit ver $VER\n";
}
my $BODY = "";
my $GROUP = "";
my $PASSWD = "";
my $TIME = "";
my $INDEX = ""; # File name of index
my $INDEX_DAY = 1; # Daily index (for $INDEX_TYPE)
my $INDEX_HOUR = 2;
my $INDEX_TYPE = $INDEX_DAY; # Saved to type of index
my $COMMA = 0; # Comma delimited output
my $iso8601 = 0;
my $month_num = 0;
my $header = 0;
my $in_seconds = 0;
my $out_seconds = 0;
my %timestr2macstr;
my %file2other;
my %gid2names = ();
my %uid2names = ();
my $_HAS_DATETIME_TIMEZONE = 0;
eval "use DateTime::TimeZone";
if ($@) {
$_HAS_DATETIME_TIMEZONE = 0;
} else {
$_HAS_DATETIME_TIMEZONE = 1;
}
sub get_timezone_list() {
my @t_list;
if ( ! $_HAS_DATETIME_TIMEZONE ) {
return @t_list;
}
foreach ( DateTime::TimeZone->all_names() ) {
push( @t_list, $_ );
}
foreach( keys( %{DateTime::TimeZone->links()}) ) {
push( @t_list, $_ );
}
return sort { $a cmp $b } @t_list;
}
usage() if (scalar(@ARGV) == 0);
while ((scalar(@ARGV) > 0) && (($_ = $ARGV[0]) =~ /^-(.)(.*)/)) {
# Body File
if (/^-b$/) {
shift(@ARGV);
if (defined $ARGV[0]) {
$BODY = $ARGV[0];
}
else {
print "-b requires body file argument\n";
}
}
elsif (/^-d$/) {
$COMMA = 1;
}
# Group File
elsif (/^-g$/) {
shift(@ARGV);
if (defined $ARGV[0]) {
&'load_group_info($ARGV[0]);
$GROUP = $ARGV[0];
}
else {
print "-g requires group file argument\n";
usage();
}
}
# Password File
elsif (/^-p$/) {
shift(@ARGV);
if (defined $ARGV[0]) {
&'load_passwd_info($ARGV[0]);
$PASSWD = $ARGV[0];
}
else {
print "-p requires password file argument\n";
usage();
}
}
elsif (/^-h$/) {
$header = 1;
}
# Index File
elsif (/^-i$/) {
shift(@ARGV);
if (defined $ARGV[0]) {
# Find out what type
if ($ARGV[0] eq "day") {
$INDEX_TYPE = $INDEX_DAY;
}
elsif ($ARGV[0] eq "hour") {
$INDEX_TYPE = $INDEX_HOUR;
}
shift(@ARGV);
unless (defined $ARGV[0]) {
print "-i requires index file argument\n";
usage();
}
$INDEX = $ARGV[0];
}
else {
print "-i requires index file argument and type\n";
usage();
}
open(INDEX, ">$INDEX") or die "Can not open $INDEX";
}
elsif (/^-V$/) {
version();
exit(0);
}
elsif (/^-m$/) {
$month_num = 1;
}
elsif (/^-y$/) {
$iso8601 = 1;
}
elsif (/^-z$/) {
shift(@ARGV);
if (defined $ARGV[0]) {
my $tz = "$ARGV[0]";
if ($tz =~ m/^list$/i) {
if ($_HAS_DATETIME_TIMEZONE) {
my $txt = "
-----------------------------------
TIMEZONE LIST
-----------------------------------\n";
foreach ( get_timezone_list() ) {
$txt .= $_ . "\n";
}
print( $txt );
}
else {
print "DateTime module not loaded -- cannot list timezones\n";
}
exit(0);
}
# validate the string if we have DateTime module
elsif ($_HAS_DATETIME_TIMEZONE) {
my $realtz = 0;
foreach ( get_timezone_list() ) {
if ($tz =~ m/^$_$/i) {
$realtz = $_;
last;
}
}
if ($realtz) {
$ENV{TZ} = $realtz;
}
else {
print "invalid timezone provided. Use -z to list valid timezones.\n";
usage();
}
}
# blindly take it otherwise
else {
$ENV{TZ} = $tz;
}
}
else {
print "-z requires the time zone argument\n";
usage();
}
}
else {
print "Unknown option: $_\n";
usage();
}
shift(@ARGV);
}
# Was the time given
if (defined $ARGV[0]) {
my $t_in;
my $t_out;
$TIME = $ARGV[0];
if ($ARGV[0] =~ /\.\./) {
($t_in, $t_out) = split(/\.\./, $ARGV[0]);
}
else {
$t_in = $ARGV[0];
$t_out = 0;
}
$in_seconds = parse_isodate($t_in);
die "Invalid Date: $t_in\n" if ($in_seconds < 0);
if ($t_out) {
$out_seconds = parse_isodate($t_out);
die "Invalid Date: $t_out\n" if ($out_seconds < 0);
}
else {
$out_seconds = 0;
}
}
else {
$in_seconds = 0;
$out_seconds = 0;
}
# Print header info
print_header() if ($header == 1);
# Print the index header
if ($INDEX ne "") {
my $time_str = "";
if ($INDEX_TYPE == $INDEX_DAY) {
$time_str = "Daily";
}
else {
$time_str = "Hourly";
}
if ($BODY ne "") {
print INDEX "$time_str Summary for Timeline of $BODY\n\n";
}
else {
print INDEX "$time_str Summary for Timeline of STDIN\n\n";
}
}
read_body();
print_tl();
################ SUBROUTINES ##################
#convert yyyy-mm-dd string to Unix date
sub parse_isodate {
my $iso_date = shift;
my $sec = 0;
my $min = 0;
my $hour = 0;
my $wday = 0;
my $yday = 0;
if ($iso_date =~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/) {
return mktime($sec, $min, $hour, $3, $2 - 1, $1 - 1900, $wday, $yday);
}
else {
return -1;
}
}
# Read the body file from the BODY variable
sub read_body {
# Read the body file from STDIN or the -b specified body file
if ($BODY ne "") {
open(BODY, "<$BODY") or die "Can't open $BODY";
}
else {
open(BODY, "<&STDIN") or die "Can't dup STDIN";
}
while (<BODY>) {
next if ((/^\#/) || (/^\s+$/));
chomp;
my (
$tmp1, $file, $st_ino, $st_ls,
$st_uid, $st_gid, $st_size, $st_atime,
$st_mtime, $st_ctime, $st_crtime, $tmp2
)
= &tm_split($_);
# Sanity check so that we ignore the header entries
next unless ((defined $st_ino) && ($st_ino =~ /[\d-]+/));
next unless ((defined $st_uid) && ($st_uid =~ /\d+/));
next unless ((defined $st_gid) && ($st_gid =~ /\d+/));
next unless ((defined $st_size) && ($st_size =~ /\d+/));
next unless ((defined $st_mtime) && ($st_mtime =~ /\d+/));
next unless ((defined $st_atime) && ($st_atime =~ /\d+/));
next unless ((defined $st_ctime) && ($st_ctime =~ /\d+/));
next unless ((defined $st_crtime) && ($st_crtime =~ /\d+/));
# we need *some* value in mactimes!
next if (!$st_atime && !$st_mtime && !$st_ctime && !$st_crtime);
# Skip if these are all too early
next
if ( ($st_mtime < $in_seconds)
&& ($st_atime < $in_seconds)
&& ($st_ctime < $in_seconds)
&& ($st_crtime < $in_seconds));
# add leading zeros to timestamps because we will later sort
# these using a string-based comparison
$st_mtime = sprintf("%.10d", $st_mtime);
$st_atime = sprintf("%.10d", $st_atime);
$st_ctime = sprintf("%.10d", $st_ctime);
$st_crtime = sprintf("%.10d", $st_crtime);
# Put all the times in one big array along with the inode and
# name (they are used in the final sorting)
# If the date on the file is too old, don't put it in the array
my $post = ",$st_ino,$file";
if ($out_seconds) {
$timestr2macstr{"$st_mtime$post"} .= "m"
if (
($st_mtime >= $in_seconds)
&& ($st_mtime < $out_seconds)
&& ( (!(exists $timestr2macstr{"$st_mtime$post"}))
|| ($timestr2macstr{"$st_mtime$post"} !~ /m/))
);
$timestr2macstr{"$st_atime$post"} .= "a"
if (
($st_atime >= $in_seconds)
&& ($st_atime < $out_seconds)
&& ( (!(exists $timestr2macstr{"$st_atime$post"}))
|| ($timestr2macstr{"$st_atime$post"} !~ /a/))
);
$timestr2macstr{"$st_ctime$post"} .= "c"
if (
($st_ctime >= $in_seconds)
&& ($st_ctime < $out_seconds)
&& ( (!(exists $timestr2macstr{"$st_ctime$post"}))
|| ($timestr2macstr{"$st_ctime$post"} !~ /c/))
);
$timestr2macstr{"$st_crtime$post"} .= "b"
if (
($st_crtime >= $in_seconds)
&& ($st_crtime < $out_seconds)
&& ( (!(exists $timestr2macstr{"$st_crtime$post"}))
|| ($timestr2macstr{"$st_crtime$post"} !~ /b/))
);
}
else {
$timestr2macstr{"$st_mtime$post"} .= "m"
if (
($st_mtime >= $in_seconds)
&& ( (!(exists $timestr2macstr{"$st_mtime$post"}))
|| ($timestr2macstr{"$st_mtime$post"} !~ /m/))
);
$timestr2macstr{"$st_atime$post"} .= "a"
if (
($st_atime >= $in_seconds)
&& ( (!(exists $timestr2macstr{"$st_atime$post"}))
|| ($timestr2macstr{"$st_atime$post"} !~ /a/))
);
$timestr2macstr{"$st_ctime$post"} .= "c"
if (
($st_ctime >= $in_seconds)
&& ( (!(exists $timestr2macstr{"$st_ctime$post"}))
|| ($timestr2macstr{"$st_ctime$post"} !~ /c/))
);
$timestr2macstr{"$st_crtime$post"} .= "b"
if (
($st_crtime >= $in_seconds)
&& ( (!(exists $timestr2macstr{"$st_crtime$post"}))
|| ($timestr2macstr{"$st_crtime$post"} !~ /b/))
);
}
# if the UID or GID is not in the array then add it.
# these are filled if the -p or -g options are given
$uid2names{$st_uid} = $st_uid
unless (defined $uid2names{$st_uid});
$gid2names{$st_gid} = $st_gid
unless (defined $gid2names{$st_gid});
#
# put /'s between multiple UID/GIDs
#
$uid2names{$st_uid} =~ s@\s@/@g;
$gid2names{$st_gid} =~ s@\s@/@g;
$file2other{$file} =
"$st_ls:$uid2names{$st_uid}:$gid2names{$st_gid}:$st_size";
}
close BODY;
} # end of read_body
sub print_header {
return if ($header == 0);
print "The Sleuth Kit mactime Timeline\n";
print "Input Source: ";
if ($BODY eq "") {
print "STDIN\n";
}
else {
print "$BODY\n";
}
print "Time: $TIME\t\t" if ($TIME ne "");
if ($ENV{TZ} eq "") {
print "\n";
}
else {
print "Timezone: $ENV{TZ}\n";
}
print "passwd File: $PASSWD" if ($PASSWD ne "");
if ($GROUP ne "") {
print "\t" if ($PASSWD ne "");
print "group File: $GROUP";
}
print "\n" if (($PASSWD ne "") || ($GROUP ne ""));
print "\n";
}
#
# Print the time line
#
sub print_tl {
my $prev_day = ""; # has the format of 'day day_week mon year'
my $prev_hour = ""; # has just the hour and is used for hourly index
my $prev_cnt = 0;
my $old_date_string = "";
my $delim = ":";
if ($COMMA != 0) {
print "Date,Size,Type,Mode,UID,GID,Meta,File Name\n";
$delim = ",";
}
# Cycle through the files and print them in sorted order.
# Note that we sort using a string comparison because the keys
# also contain the inode and file name
for my $key (sort { $a cmp $b } keys %timestr2macstr) {
my $time;
my $inode;
my $file;
if ($key =~ /^(\d+),([\d-]+),(.*)$/) {
$time = $1;
$inode = $2;
$file = $3;
}
else {
next;
}
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
if ($iso8601) {
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
gmtime($time);
}
else {
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
localtime($time);
}
# the month here is 0-11, not 1-12, like what we want
$mon++;
print
"\t($sec,$min,$hour,MDay: $mday,M: $mon,$year,$wday,$yday,$isdst) = ($time)\n"
if $debug;
#
# cosmetic change to make it look like unix dates
#
$mon = "0$mon" if $mon < 10;
$mday = "0$mday" if $mday < 10;
$hour = "0$hour" if $hour < 10;
$min = "0$min" if $min < 10;
$sec = "0$sec" if $sec < 10;
my $yeart = $year + 1900;
# How do we print the date?
#
my $date_string;
if ($iso8601) {
if ($time == 0) {
$date_string = "0000-00-00T00:00:00Z";
}
else {
$date_string =
"$yeart-$mon-${mday}T$hour:$min:${sec}Z";
}
}
else {
if ($time == 0) {
$date_string = "Xxx Xxx 00 0000 00:00:00";
}
elsif ($month_num) {
$date_string =
"$digit_to_day{$wday} $mon $mday $yeart $hour:$min:$sec";
}
else {
$date_string =
"$digit_to_day{$wday} $digit_to_month{$mon} $mday $yeart $hour:$min:$sec";
}
}
#
# However, we only print the date if it's different from the one
# above. We need to fill the empty space with blanks, though.
#
if ($old_date_string eq $date_string) {
if ($iso8601) {
$date_string = " ";
}
else {
$date_string = " ";
}
$prev_cnt++
if ($INDEX ne "");
}
else {
$old_date_string = $date_string;
# Indexing code
if ($INDEX ne "") {
# First time it is run
if ($prev_day eq "") {
$prev_day = "$mday $wday $mon $yeart";
$prev_hour = $hour;
$prev_cnt = 0;
}
# A new day, so print the results
elsif ($prev_day ne "$mday $wday $mon $yeart") {
my @prev_vals = split(/ /, $prev_day);
my $date_str;
if ($month_num) {
$date_str =
"$digit_to_day{$prev_vals[1]} "
. "$prev_vals[2] "
. "$prev_vals[0] ${prev_vals[3]}";
}
else {
$date_str =
"$digit_to_day{$prev_vals[1]} "
. "$digit_to_month{$prev_vals[2]} "
. "$prev_vals[0] ${prev_vals[3]}";
}
$date_str .= " $prev_hour:00:00"
if ($INDEX_TYPE == $INDEX_HOUR);
print INDEX "${date_str}${delim} $prev_cnt\n";
# Reset
$prev_cnt = 0;
$prev_day = "$mday $wday $mon $yeart";
$prev_hour = $hour;
}
# Same day, but new hour
elsif (($INDEX_TYPE == $INDEX_HOUR) && ($prev_hour != $hour)) {
my @prev_vals = split(/ /, $prev_day);
if ($month_num) {
print INDEX "$digit_to_day{$prev_vals[1]} "
. "$prev_vals[2] "
. "$prev_vals[0] ${prev_vals[3]} "
. "$prev_hour:00:00${delim} $prev_cnt\n";
}
else {
print INDEX "$digit_to_day{$prev_vals[1]} "
. "$digit_to_month{$prev_vals[2]} "
. "$prev_vals[0] ${prev_vals[3]} "
. "$prev_hour:00:00${delim} $prev_cnt\n";
}
# Reset
$prev_cnt = 0;
$prev_hour = $hour;
}
$prev_cnt++;
}
}
#
# Muck around with the [mac]times string to make it pretty.
#
my $mactime_tmp = $timestr2macstr{$key};
my $mactime = "";
if ($mactime_tmp =~ /m/) {
$mactime = "m";
}
else {
$mactime = ".";
}
if ($mactime_tmp =~ /a/) {
$mactime .= "a";
}
else {
$mactime .= ".";
}
if ($mactime_tmp =~ /c/) {
$mactime .= "c";
}
else {
$mactime .= ".";
}
if ($mactime_tmp =~ /b/) {
$mactime .= "b";
}
else {
$mactime .= ".";
}
my ($ls, $uids, $groups, $size) = split(/:/, $file2other{$file});
print "FILE: $file MODES: $ls U: $uids G: $groups S: $size\n"
if $debug;
if ($COMMA == 0) {
printf("%s %8s %3s %s %-8s %-8s %-8s %s\n",
$date_string, $size, $mactime, $ls, $uids, $groups, $inode,
$file);
}
else {
# escape any quotes in filename
my $file_tmp = $file;
$file_tmp =~ s/\"/\"\"/g;
printf("%s,%s,%s,%s,%s,%s,%s,\"%s\"\n",
$old_date_string, $size, $mactime, $ls, $uids, $groups, $inode,
$file_tmp);
}
}
# Finish the index page for the last entry
if (($INDEX ne "") && ($prev_cnt > 0)) {
my @prev_vals = split(/ /, $prev_day);
my $date_str;
if ($month_num) {
$date_str =
"$digit_to_day{$prev_vals[1]} "
. "$prev_vals[2] "
. "$prev_vals[0] ${prev_vals[3]}";
}
else {
$date_str =
"$digit_to_day{$prev_vals[1]} "
. "$digit_to_month{$prev_vals[2]} "
. "$prev_vals[0] ${prev_vals[3]}";
}
$date_str .= " $prev_hour:00:00"
if ($INDEX_TYPE == $INDEX_HOUR);
print INDEX "${date_str}${delim} $prev_cnt\n";
close INDEX;
}
}
#
# Routines for reading and caching user and group information. These
# are used in multiple programs... it caches the info once, then hopefully
# won't be used again.
#
# Steve Romig, May 1991.
#
# Provides a bunch of routines and a bunch of arrays. Routines
# (and their usage):
#
# load_passwd_info($use_getent, $file_name)
#
# loads user information into the %uname* and %uid* arrays
# (see below).
#
# If $use_getent is non-zero:
# get the info via repeated 'getpwent' calls. This can be
# *slow* on some hosts, especially if they are running as a
# YP (NIS) client.
# If $use_getent is 0:
# if $file_name is "", then get the info from reading the
# results of "ypcat passwd" and from /etc/passwd. Otherwise,
# read the named file. The file should be in passwd(5)
# format.
#
# load_group_info($use_gentent, $file_name)
#
# is similar to load_passwd_info.
#
# Information is stored in several convenient associative arrays:
#
# %uid2names Assoc array, indexed by uid, value is list of
# user names with that uid, in form "name name
# name...".
#
# %gid2members Assoc array, indexed by gid, value is list of
# group members in form "name name name..."
#
# %gname2gid Assoc array, indexed by group name, value is
# matching gid.
#
# %gid2names Assoc array, indexed by gid, value is the
# list of group names with that gid in form
# "name name name...".
#
# You can also use routines named the same as the arrays - pass the index
# as the arg, get back the value. If you use this, get{gr|pw}{uid|gid|nam}
# will be used to lookup entries that aren't found in the cache.
#
# To be done:
# probably ought to add routines to deal with full names.
# maybe there ought to be some anal-retentive checking of password
# and group entries.
# probably ought to cache get{pw|gr}{nam|uid|gid} lookups also.
# probably ought to avoid overwriting existing entries (eg, duplicate
# names in password file would collide in the tables that are
# indexed by name).
#
# Disclaimer:
# If you use YP and you use netgroup entries such as
# +@servers::::::
# +:*:::::/usr/local/utils/messages
# then loading the password file in with &load_passwd_info(0) will get
# you mostly correct YP stuff *except* that it won't do the password and
# shell substitutions as you'd expect. You might want to use
# &load_passwd_info(1) instead to use getpwent calls to do the lookups,
# which would be more correct.
#
#
# minor changes to make it fit with the TCT program, 9/25/99, - dan
# A whole lot removed to clean it up for TSK - July 2008 - Brian
#
package main;
my $passwd_loaded = 0; # flags to use to avoid reloading everything
my $group_loaded = 0; # unnecessarily...
#
# Update user information for the user named $name. We cache the password,
# uid, login group, home directory and shell.
#
sub add_pw_info {
my ($name, $tmp, $uid) = @_;
if ((defined $name) && ($name ne "")) {
if ((defined $uid) && ($uid ne "")) {
if (defined($uid2names{$uid})) {
$uid2names{$uid} .= " $name";
}
else {
$uid2names{$uid} = $name;
}
}
}
}
#
# Update group information for the group named $name. We cache the gid
# and the list of group members.
#
sub add_gr_info {
my ($name, $tmp, $gid) = @_;
if ((defined $name) && ($name ne "")) {
if ((defined $gid) && ($gid ne "")) {
if (defined($gid2names{$gid})) {
$gid2names{$gid} .= " $name";
}
else {
$gid2names{$gid} = $name;
}
}
}
}
sub load_passwd_info {
my ($file_name) = @_;
my (@pw_info);
if ($passwd_loaded) {
return;
}
$passwd_loaded = 1;
open(FILE, $file_name)
|| die "can't open $file_name";
while (<FILE>) {
chop;
if ($_ !~ /^\+/) {
&add_pw_info(split(/:/));
}
}
close(FILE);
}
sub load_group_info {
my ($file_name) = @_;
my (@gr_info);
if ($group_loaded) {
return;
}
$group_loaded = 1;
open(FILE, $file_name)
|| die "can't open $file_name";
while (<FILE>) {
chop;
if ($_ !~ /^\+/) {
&add_gr_info(split(/:/));
}
}
close(FILE);
}
#
# Split a time machine record.
#
sub tm_split {
my ($line) = @_;
my (@fields);
for (@fields = split(/\|/, $line)) {
s/%([A-F0-9][A-F0-9])/pack("C", hex($1))/egis;
}
return @fields;
}
1;

View File

@ -1046,7 +1046,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar,
pathToBodyFile = PlatformUtil.getOSFilePath(pathToBodyFile);
if (PlatformUtil.isWindowsOS()) {
macpath = machome + java.io.File.separator + "mactime.exe";
cmdpath = PlatformUtil.getOSFilePath(cmdpath);
cmdpath = PlatformUtil.getOSFilePath(macpath);
mactimeArgs = new String[]{"-b", pathToBodyFile, "-d", "-y"};
} else {
cmdpath = "perl";

View File

@ -71,20 +71,13 @@
<var name="extra-bin-name" value="${app.name}64.exe"/>
<var name="aut-bin-name" value="${app.name}.exe"/>
<var name="jre-path" value="${env.JRE_HOME_32}"/>
<var name="package-type" value="x86" />
<antcall target="run-ai-32" inheritAll="true" inheritRefs="true" />
<delete dir="${nbdist.dir}/installer_${app.name}_32-cache"/>
<move file="${nbdist.dir}/installer_${app.name}_32-SetupFiles/installer_${app.name}_32.msi" tofile="${nbdist.dir}/installer_${app.name}_32-${app.version}.msi" />
</target>
<target name="run-ai-32" depends="add-ai-productinfo,add-ai-files,add-ai-jre,add-ai-shortcuts,add-ai-env" description="Builds the 64 bit installer.">
<!-- Leaving this commented out bit for documentation purposes. Not sure what its supposed to do. -->
<!-- Need to find a way to deal with beta version -->
<!--<echo message="Setting ${app.name} version to ${app.version}..."/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /SetVersion ${app.version}"/>
</exec>-->
<!--<delete file="${aip-path}"/>-->
</target>
<target name="run-ai-32" depends="add-ai-productinfo,add-ai-files,add-ai-jre,add-ai-shortcuts,add-ai-env,ai-build" description="Builds the 32 bit installer."/>
<target name="check-ai-64" if="jre.home.64" description="Builds the 64 bit installer IF JRE_HOME_64 is set.">
<property environment="env"/>
@ -92,13 +85,14 @@
<var name="extra-bin-name" value="${app.name}.exe"/>
<var name="aut-bin-name" value="${app.name}64.exe"/>
<var name="jre-path" value="${env.JRE_HOME_64}"/>
<var name="package-type" value="x64" />
<echo message="aip-path: ${aip-path}" />
<antcall target="run-ai-64" inheritAll="true" inheritRefs="true" />
<delete dir="${nbdist.dir}/installer_${app.name}_64-cache"/>
<move file="${nbdist.dir}/installer_${app.name}_64-SetupFiles/installer_${app.name}_64.msi" tofile="${nbdist.dir}/installer_${app.name}_64-${app.version}.msi" />
</target>
<target name="run-ai-64" depends="add-ai-productinfo,add-ai-files,add-ai-jre,add-ai-shortcuts,add-ai-env" description="Builds the 64 bit installer."/>
<target name="run-ai-64" depends="add-ai-productinfo,add-ai-files,add-ai-jre,add-ai-shortcuts,ai-set-type,ai-build" description="Builds the 64 bit installer."/>
<target name="add-ai-productinfo" description="Add product information to the aip file">
<scriptdef name="generateguid" language="javascript">
@ -185,8 +179,17 @@
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /NewEnvironment -name PATH -value %GSTREAMER_PATH% -install_operation CreateUpdate -behavior Append -system_variable"/>
</exec>
</target>
<target name="ai-build" description="Build the installer.">
<exec executable="${ai-exe-path}">
<arg line="/build ${aip-path}"/>
</exec>
</target>
<target name="ai-set-type" description="Set the installer to only install on 64 bit machines.">
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /SetPackageType ${package-type}"/>
</exec>
</target>
</project>