diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel2.java index 9f269f60a6..333f4e9db4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel2.java @@ -53,6 +53,7 @@ final class AddImageVisualPanel2 extends JPanel { infoPanel.add(progressLabel); infoPanel.add(Box.createRigidArea(new Dimension(10, 10))); //spacer this.jScrollPane1.setBorder(null); + this.TextArea_CurrentDirectory.setBackground(this.getBackground()); } void resetInfoPanel() { @@ -108,7 +109,15 @@ final class AddImageVisualPanel2 extends JPanel { * @param dir the text to update with */ public void changeCurrentDir(String dir){ - this.TextArea_CurrentDirectory.setText(dir.trim().isEmpty() ? "Folder Information Unavailable" : dir); + this.TextArea_CurrentDirectory.setText(dir); + } + + /** + * Sets the CurrentlyProcessing tag and text area to be invisible + */ + public void setProcessInvis(){ + this.Label_CurrentDirectory_Static.setVisible(false); + this.TextArea_CurrentDirectory.setVisible(false); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java index db3b232eb7..50ef1ebc0e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java @@ -202,26 +202,31 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { * Class for getting the currently processing directory. * */ - private class CurrentDirectoryFetcher extends SwingWorker { - + + private static class CurrentDirectoryFetcher extends SwingWorker { AddImgTask task; - - CurrentDirectoryFetcher(AddImgTask task) { - this.task = task; + JProgressBar prog; + AddImageVisualPanel2 wiz; + AddImageProcess proc; + + CurrentDirectoryFetcher(JProgressBar prog, AddImageVisualPanel2 wiz, AddImageProcess proc){ + this.wiz = wiz; + this.proc = proc; + this.prog = prog; } /** * @return the currently processing directory */ @Override - protected Integer doInBackground() { - try { - while (task.progressBar.getValue() < 100 || task.progressBar.isIndeterminate()) { - + protected Integer doInBackground(){ + try{ + while(prog.getValue() < 100 || prog.isIndeterminate()){ //TODO Rely on state variable in AddImgTask class + EventQueue.invokeLater(new Runnable() { @Override public void run() { - wizPanel.getComponent().changeCurrentDir(process.currentDirectory()); + wiz.changeCurrentDir(proc.currentDirectory()); } }); @@ -232,8 +237,18 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { return -1; } } + + /** + * When done, set the Wizards processing tags to be invisible + */ + @Override + protected void done() { + wiz.setProcessInvis(); + } } + + /** * Thread that will make the JNI call to ingest the image. */ @@ -298,7 +313,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { process = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans); - fetcher = new CurrentDirectoryFetcher(this); + fetcher = new CurrentDirectoryFetcher(this.progressBar, wizPanel.getComponent(), process); cancelledWhileRunning.enable(); try { wizPanel.setStateStarted(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index fb89e36299..a599372982 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -165,11 +165,13 @@ public class Case { String oldCaseName = oldCase != null ? oldCase.name : ""; + doCaseChange(null); //closes windows, etc pcs.firePropertyChange(CASE_CURRENT_CASE, oldCase, null); - doCaseChange(null); + - pcs.firePropertyChange(CASE_NAME, oldCaseName, ""); doCaseNameChange(""); + pcs.firePropertyChange(CASE_NAME, oldCaseName, ""); + @@ -827,14 +829,16 @@ public class Case { Case.runAddImageAction(); } } else { // case is closed + // close all top components first + CoreComponentControl.closeCoreWindows(); + // disable these menus CallableSystemAction.get(AddImageAction.class).setEnabled(false); // Add Image menu CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu - // close all top components - CoreComponentControl.closeCoreWindows(); + Frame f = WindowManager.getDefault().getMainWindow(); f.setTitle(Case.getAppName()); // set the window name to just application name diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseSchema.xsd b/Core/src/org/sleuthkit/autopsy/casemodule/CaseSchema.xsd index 41e3724f69..30a86d859a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseSchema.xsd +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseSchema.xsd @@ -6,9 +6,9 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index 10aaeccf1d..091ac4797e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel; import javax.swing.Action; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.directorytree.ExtractUnallocAction; import org.sleuthkit.datamodel.Volume; /** @@ -66,6 +67,7 @@ public class VolumeNode extends AbstractContentNode { public Action[] getActions(boolean popup) { return new Action[]{ //new ShowDetailAction("Volume Details", this.getName(), this), //new ShowDetailAction("File System Details", this.getName(), this) + //new ExtractUnallocAction("Extract Unallocated Files to single Single", this) }; } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 36b10f81a7..e0ad518535 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -160,7 +160,6 @@ public class DataResultFilterNode extends FilterNode { List actions = new ArrayList(); actions.add(new NewWindowViewAction("View in New Window", vol)); actions.addAll(ShowDetailActionVisitor.getActions(vol.getLookup().lookup(Content.class))); - return actions; } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java new file mode 100644 index 0000000000..f8018115ed --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java @@ -0,0 +1,279 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier sleuthkit 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.directorytree; + +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.Cancellable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.VolumeNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.FileSystem; +import org.sleuthkit.datamodel.LayoutDirectory; +import org.sleuthkit.datamodel.LayoutFile; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.Volume; + +/** + * Extracts all the unallocated space as a single file + */ +public final class ExtractUnallocAction extends AbstractAction{ + + private List llf; + long VolumeId; + String ImageName; + long ImageId; + volatile static boolean running = false; + private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName()); + + public ExtractUnallocAction(String title, Volume volu){ + super(title); + VolumeId = volu.getId(); + try{ + ImageName = volu.getImage().getName(); + ImageId = volu.getImage().getId(); + } catch(TskCoreException tce){ + logger.log(Level.WARNING, "Unable to properly create ExtractUnallocAction, extraction may be incomplete", tce); + ImageName = ""; + ImageId = 0; + } + llf = getUnallocFiles(volu); + Collections.sort(llf, new SortObjId()); + } + + /** + * Writes the unallocated files to $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat + * @param e + */ + @Override + public void actionPerformed(ActionEvent e) { + if (llf != null && llf.size() > 0) { + String UnallocName = ImageName + "-Unalloc-" + ImageId + "-" + VolumeId + ".dat"; + //Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat + File unalloc = new File(Case.getCurrentCase().getCaseDirectory() + File.separator + "Export" + File.separator + UnallocName); + if(running){ + JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already running on this volume. Please select a different volume."); + return; + } + if (unalloc.exists()) { + int res = JOptionPane.showConfirmDialog(new Frame(), "The Unalloc File for this volume, " + UnallocName + " already exists, do you want to replace it?"); + if (res == JOptionPane.YES_OPTION) { + unalloc.delete(); + } else { + return; + } + } + ExtractUnallocWorker uw = new ExtractUnallocWorker(unalloc); + uw.execute(); + } else { + logger.log(Level.WARNING, "Tried to get unallocated content from volume ID " + VolumeId + ", but its list of unallocated files was empty or null"); + } + } + + private List getUnallocFiles(Content c) { + UnallocVisitor uv = new UnallocVisitor(); + logger.log(Level.INFO, "Sending out Unallocated File Visitor"); + try { + return c.getChildren().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); + } + return null; + } + + + /** + * Private class for dispatching the file IO in a background thread. + */ + private class ExtractUnallocWorker extends SwingWorker { + + File path; + private ProgressHandle progress; + private boolean canceled = false; + + ExtractUnallocWorker(File path) { + this.path = path; + running = true; + } + + @Override + protected Integer doInBackground() { + try { + progress = ProgressHandleFactory.createHandle("Extracting " + path.getName(), new Cancellable() { + @Override + public boolean cancel() { + logger.log(Level.INFO, "Canceling extraction of Unalloc file " + path.getName()); + canceled = true; + if (progress != null) { + progress.setDisplayName(path.getName() + " (Cancelling...)"); + } + return true; + } + }); + FileOutputStream fos = new FileOutputStream(path); + int MAX_BYTES = 8192; + byte[] buf = new byte[MAX_BYTES]; //read 8k at a time + logger.log(Level.INFO, "Writing Unalloc file to " + path.getPath()); + + progress.start(llf.size()); + int count = 0; + for (LayoutFile f : llf) { + long offset = 0L; + while (offset != f.getSize() && !canceled) { + offset += f.read(buf, offset, MAX_BYTES); //Offset + Bytes read + fos.write(buf); + } + progress.progress(count++); + } + progress.finish(); + fos.flush(); + fos.close(); + + if(canceled){ + path.delete(); + logger.log(Level.INFO, "Canceled extraction of " + path.getName() + " and deleted file"); + } + else{ + logger.log(Level.INFO, "Finished writing unalloc file"); + } + } catch (IOException ioe) { + logger.log(Level.WARNING, "Could not create Unalloc File; error writing file", ioe); + return -1; + } catch (TskCoreException tce) { + logger.log(Level.WARNING, "Could not create Unalloc File; error getting image info", tce); + return -1; + }finally{ + running = false; + } + return 1; + } + } + + private static class UnallocVisitor extends ContentVisitor.Default> { + + /** + * If the volume has no FileSystem, then it will call this method to return the single instance of unallocated space. + * @param lf the LayoutFile the visitor encountered + * @return A list of size 1, returns null if it fails + */ + @Override + public List visit(final org.sleuthkit.datamodel.LayoutFile lf) { + return new ArrayList() { + { + add(lf); + } + }; + } + + /** + * If the visitor finds a FileSystem, it will filter the results for directories and return on the Root Dir. + * @param fs the FileSystem the visitor encountered + * @return A list containing the layout files from subsequent Visits(), returns null if it fails + */ + @Override + public List visit(FileSystem fs) { + try { + for (Content c : fs.getChildren()){ + if(((AbstractFile)c).isRoot()){ + return c.accept(this); + } + } + } catch (TskCoreException tce) { + logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting FileSystem " + fs.getId(), tce); + } + return null; + } + + /** + * LayoutDirectory has all the Layout(Unallocated) files + * @param ld LayoutDirectory the visitor encountered + * @return A list containing all the LayoutFile in ld, returns null if it fails + */ + @Override + public List visit(LayoutDirectory ld){ + try{ + List lflst = new ArrayList(); + for(Content layout : ld.getChildren()){ + lflst.add((LayoutFile)layout); + } + return lflst; + } catch(TskCoreException tce){ + logger.log(Level.WARNING, "Could not get list of Layout Files, failed at visiting Layout Directory", tce); + } + return null; + } + + /** + * The only time this visitor should ever encounter a directory is when parsing over Root + * @param dir the directory this visitor encountered + * @return A list containing LayoutFiles encountered during subsequent Visits(), returns null if it fails + */ + @Override + public List visit(Directory dir) { + try { + for (Content c : dir.getChildren()) { + if(c instanceof LayoutDirectory){ + return c.accept(this); + } + } + }catch (TskCoreException tce) { + logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting Directory " + dir.getId(), tce); + } + return null; + } + + @Override + protected List defaultVisit(Content cntnt) { + return null; + } + } + + private class SortObjId implements Comparator{ + + @Override + public int compare(LayoutFile o1, LayoutFile o2) { + if(o1.getId() == o2.getId()){ + return 0; + } + if(o1.getId() > o2.getId()){ + return -1; + } + else{ + return 1; + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ShowDetailActionVisitor.java b/Core/src/org/sleuthkit/autopsy/directorytree/ShowDetailActionVisitor.java index c539f9b0de..537b127da2 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ShowDetailActionVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ShowDetailActionVisitor.java @@ -44,11 +44,7 @@ import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Volume; -/** - * - * - * @author jantonius - */ + class ShowDetailActionVisitor extends ContentVisitor.Default> { private static ShowDetailActionVisitor instance = new ShowDetailActionVisitor(); @@ -231,11 +227,32 @@ class ShowDetailActionVisitor extends ContentVisitor.Default visit(final Volume vol) { - final String title = "Volume Details"; + List lst = new ArrayList(); + lst.add(new VolumeDetailsAction("Volume Details", vol)); + lst.add(new ExtractUnallocAction("Extract Unallocated Space to Single File", vol)); + return lst; + } + - return Collections.singletonList(new AbstractAction(title) { + @Override + protected List defaultVisit(Content di) { + return new ArrayList(); + } +} - @Override + + class VolumeDetailsAction extends AbstractAction{ + + String title; + Volume vol; + + VolumeDetailsAction(String title, Volume vol){ + super(title); + this.title = title; + this.vol = vol; + } + + @Override public void actionPerformed(ActionEvent e) { Logger.noteAction(ShowDetailActionVisitor.class); @@ -289,11 +306,4 @@ class ShowDetailActionVisitor extends ContentVisitor.Default defaultVisit(Content di) { - return new ArrayList(); - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index eb3d241288..2a59d671df 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -69,6 +69,8 @@ public class HashDbIngestModule implements IngestModuleAbstractFile { static long lookuptime = 0; private Map knownBadSets = new HashMap(); private HashDbManagementPanel panel; + + private final Hash hasher = new Hash(); private HashDbIngestModule() { count = 0; @@ -332,7 +334,7 @@ public class HashDbIngestModule implements IngestModuleAbstractFile { String md5Hash = fsContent.getMd5Hash(); if (md5Hash == null || md5Hash.isEmpty()) { long calcstart = System.currentTimeMillis(); - md5Hash = Hash.calculateMd5(fsContent); + md5Hash = hasher.calculateMd5(fsContent); calctime += (System.currentTimeMillis() - calcstart); } TskData.FileKnown status = TskData.FileKnown.UKNOWN; @@ -374,7 +376,7 @@ public class HashDbIngestModule implements IngestModuleAbstractFile { String md5Hash = fsContent.getMd5Hash(); if (md5Hash == null || md5Hash.isEmpty()) { long calcstart = System.currentTimeMillis(); - Hash.calculateMd5(fsContent); + hasher.calculateMd5(fsContent); calctime += (System.currentTimeMillis() - calcstart); } ret = ProcessResult.OK; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchAction.java index a739a1259a..f80e9063be 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchAction.java @@ -32,21 +32,21 @@ import org.sleuthkit.datamodel.FsContent; /** * Searches for FsContent Files with the same MD5 hash as the given Node's * FsContent's MD5 hash. This action should only be available from Nodes with - * specific Content attached; it is manually programmed into a Node's available actions. + * specific Content attached; it is manually programmed into a Node's available + * actions. */ public class HashDbSearchAction extends CallableSystemAction implements HashSearchProvider { private static final InitializeContentVisitor initializeCV = new InitializeContentVisitor(); private FsContent fsContent; - private static HashDbSearchAction instance = null; HashDbSearchAction() { super(); } - + public static HashDbSearchAction getDefault() { - if(instance == null){ + if (instance == null) { instance = new HashDbSearchAction(); } return instance; @@ -60,7 +60,7 @@ public class HashDbSearchAction extends CallableSystemAction implements HashSear } /** - * Returns the FsContent if it is supported, otherwise null. It should + * Returns the FsContent if it is supported, otherwise null. It should * realistically never return null or a Directory, only a File. */ private static class InitializeContentVisitor extends ContentVisitor.Default { @@ -82,28 +82,21 @@ public class HashDbSearchAction extends CallableSystemAction implements HashSear } /** - * Find all files with the same MD5 hash as this' fsContent. fsContent should - * be previously set by calling the search function, which in turn calls performAction. + * Find all files with the same MD5 hash as this' fsContent. fsContent + * should be previously set by calling the search function, which in turn + * calls performAction. */ @Override public void performAction() { - // Make sure all files have an md5 hash - if(HashDbSearcher.allFilesMd5Hashed()) { + // Make sure at least 1 file has an md5 hash + if (HashDbSearcher.countFilesMd5Hashed() > 0) { doSearch(); - // and if not, warn the user - } else if(HashDbSearcher.countFilesMd5Hashed() > 0) { - Object selected = JOptionPane.showConfirmDialog(null, "Not all files have MD5 hashes. " - + "Search results will be incomplete.\n" - + "Would you like to search anyway?", "File Search by MD5 Hash", JOptionPane.YES_NO_OPTION); - if(selected.equals(JOptionPane.YES_OPTION)) { - doSearch(); - } } else { - JOptionPane.showMessageDialog(null, "No files currently have an MD5 hash.", - "File Search by MD5 Hash", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "No files currently have an MD5 hash calculated, run HashDB ingest first.", + "File Search by MD5 Hash", JOptionPane.ERROR_MESSAGE); } } - + private void doSearch() { HashDbSearchThread hashThread = new HashDbSearchThread(fsContent); hashThread.execute(); diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchPanel.java index cc79736d54..7c50fc25f6 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSearchPanel.java @@ -292,20 +292,9 @@ public class HashDbSearchPanel extends javax.swing.JPanel implements ActionListe boolean search() { // Check if any hashed have been entered if(hashTable.getRowCount()!=0) { - // Make sure all files have an md5 hash - if(HashDbSearcher.allFilesMd5Hashed()) { - return doSearch(); - // and if not, warn the user - } else if(HashDbSearcher.countFilesMd5Hashed() > 0) { - errorField.setVisible(false); - Object selected = JOptionPane.showConfirmDialog(null, "Not all files have MD5 hashes. " - + "Search results will be incomplete.\n" - + "Would you like to search anyway?", "File Search by MD5 Hash", JOptionPane.YES_NO_OPTION); - if(selected.equals(JOptionPane.YES_OPTION)) { - return doSearch(); - } else { - return false; - } + // Make sure at least 1 file has an md5 hash + if(HashDbSearcher.countFilesMd5Hashed() > 0) { + return doSearch(); } else { JOptionPane.showMessageDialog(null, "No files currently have an MD5 hash.", "File Search by MD5 Hash", JOptionPane.ERROR_MESSAGE); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java index 567d6fcaaa..b9894f83f5 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java @@ -25,6 +25,7 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JMenuItem; import javax.swing.SwingWorker; @@ -39,6 +40,7 @@ import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit.HTMLFactory; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.TextUtil; /** @@ -320,7 +322,7 @@ class ExtractedContentPanel extends javax.swing.JPanel { */ void setSources(List sources) { sourceComboBox.removeAllItems(); - setPanelText(null); + setPanelText(null, false); for (MarkupSource ms : sources) { sourceComboBox.addItem(ms); @@ -351,16 +353,29 @@ class ExtractedContentPanel extends javax.swing.JPanel { return (MarkupSource) sourceComboBox.getSelectedItem(); } - private void setPanelText(String text) { - if (text == null ) { + private void setPanelText(String text, boolean detectDirection) { + if (text == null) { text = ""; } - - //detect text direction using first 1024 chars and set it - final int maxOrientChars = Math.min(text.length(), 1024); - final String orientDetectText = text.substring(0, maxOrientChars); - extractedTextPane.applyComponentOrientation(TextUtil.getTextDirection(orientDetectText)); - + + if (detectDirection) { + //detect text direction using first 1024 chars and set it + //get first up to 1024 chars, strip
 tag and unescape html to get the string on which to detect
+            final int len = text.length();
+            final int prefixLen = "
".length();
+            if (len > prefixLen) {
+                final int maxOrientChars = Math.min(len, 1024);
+                final String orientDetectText = EscapeUtil.unEscapeHtml(text.substring(prefixLen, maxOrientChars));
+                ComponentOrientation direction = TextUtil.getTextDirection(orientDetectText);
+                //logger.log(Level.INFO, "ORIENTATION LEFT TO RIGHT: " + direction.isLeftToRight());
+                extractedTextPane.applyComponentOrientation(direction);
+            } else {
+                extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+            }
+        } else {
+            extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+        }
+
         extractedTextPane.setText(text);
         extractedTextPane.setCaretPosition(0);
     }
@@ -572,16 +587,15 @@ class ExtractedContentPanel extends javax.swing.JPanel {
         }
     }
 
-    
     /**
-     * Gets and sets new markup.  Updates GUI in GUI thread and gets markup in background thread.
-     * To be invoked from GUI thread only.
+     * Gets and sets new markup. Updates GUI in GUI thread and gets markup in
+     * background thread. To be invoked from GUI thread only.
      */
     private void setMarkup(MarkupSource source) {
-        setPanelText("Loading text... Please wait");
+        setPanelText("Loading text... Please wait", false);
         new SetMarkup(source).execute();
     }
-    
+
     /**
      * Swingworker to get makrup source content String from Solr in background
      * thread and then set the panel text in the EDT Helps not to block the UI
@@ -603,7 +617,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
             progress.setDisplayName("Loading text");
             progress.start();
             progress.switchToIndeterminate();
-            
+
             markup = source.getMarkup();
             return null;
         }
@@ -613,9 +627,9 @@ class ExtractedContentPanel extends javax.swing.JPanel {
             //super.done();
             progress.finish();
             if (markup != null) {
-                setPanelText(markup);
+                setPanelText(markup, true);
             } else {
-                setPanelText("");
+                setPanelText("", false);
             }
             updateControls(source);
 
diff --git a/NEWS.txt b/NEWS.txt
index faafb83b3e..7b5223753b 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -3,11 +3,16 @@
 New features:
 
 Improvements:
-- Add Image Wizard - better work-flow, better device size reporting
-- File Ingest: reduced file queuing time and memory usage
+- File Ingest: minimized file queuing time and memory usage
+- Add Image Wizard - better work-flow, better device size reporting, info on currently processed directory
+- Added extraction of all unallocated blocks (from volume, image) as a single file
+- Reporting improvements: reorganized columns, sorted by 1st column, added logo to html report
 
 Bugfixes:
 - fixed periodic keyword search during ingest, when it'd run max. 2 times only
+- fixed Downloads "target" in Recent Activity
+- fixed missing hash and keyword search hits in reports
+
 
 ---------------- VERSION 3.0.1 --------------
 
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
index 3b87a9417a..51e679b826 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java
@@ -282,8 +282,6 @@ public class Firefox extends Extract implements IngestModuleImage {
     // This gets the downloads info
     private void getDownload(Image image, IngestImageWorkerController controller) {
 
-        //List downloadsFiles = this.extractFiles(image, "select * from tsk_files where name LIKE 'downloads.sqlite' and name NOT LIKE '%journal%' and parent_path LIKE '%Firefox%'");
-        
         FileManager fileManager = currentCase.getServices().getFileManager();
         List downloadsFiles = null;
         try {
@@ -291,7 +289,7 @@ public class Firefox extends Extract implements IngestModuleImage {
         } catch (TskCoreException ex) {
             logger.log(Level.WARNING, "Error fetching 'downloads' files for Firefox.");
         }
-        
+
         if (downloadsFiles == null) {
             return;
         }
@@ -325,7 +323,7 @@ public class Firefox extends Extract implements IngestModuleImage {
                     //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()))));
                     bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID(), "RecentActivity", Util.findID(image, urldecodedtarget)));
-                    bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH.getTypeID(), "RecentActivity", urldecodedtarget));
+                    bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH.getTypeID(), "RecentActivity", ((result.get("target").toString() != null) ? result.get("target").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("source").toString() != null) ? result.get("source").toString() : ""))));
                     this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
@@ -337,10 +335,11 @@ public class Firefox extends Extract implements IngestModuleImage {
             if (errors > 0) {
                 this.addErrorMessage(this.getName() + ": Error parsing " + errors + " Firefox web history artifacts.");
             }
-            ++j;
+            j++;
             dbFile.delete();
+            break;
         }
-
+        
         services.fireModuleDataEvent(new ModuleDataEvent("Recent Activity", BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD));
     }