From dcd6ff210860d518ea168a4d2e03290e57e67aba Mon Sep 17 00:00:00 2001 From: dhurd Date: Thu, 19 Jul 2012 09:15:15 -0400 Subject: [PATCH 1/2] Addition of 0xNF's config file for the tester. --- Testing/script/config.xml | 13 +++ Testing/script/regression.py | 182 +++++++++++++++++++++++++---------- 2 files changed, 142 insertions(+), 53 deletions(-) create mode 100644 Testing/script/config.xml diff --git a/Testing/script/config.xml b/Testing/script/config.xml new file mode 100644 index 0000000000..3df05718e5 --- /dev/null +++ b/Testing/script/config.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/Testing/script/regression.py b/Testing/script/regression.py index 42db4ba743..795b5a26b6 100644 --- a/Testing/script/regression.py +++ b/Testing/script/regression.py @@ -1,4 +1,5 @@ #!/usr/bin/python +#en_US.UTF-8 import sys import sqlite3 import re @@ -6,18 +7,24 @@ import subprocess import os.path import shutil import time +import xml +from xml.dom.minidom import parse, parseString -# Last modified 7/13/12 3@ pm -# Usage: ./regression.py [-i FILE] [OPTIONS] + +# Last modified 7/17/12 @5pm +# Usage: ./regression.py [-s FILE] OR [-l CONFIG] [OPTIONS] # Run the RegressionTest.java file, and compare the result with a gold standard # When the -i flag is set, this script only tests the image given by FILE. -# By default, it tests every image in ./input/ # An indexed NSRL database is expected at ./input/nsrl.txt-md5.idx, # and an indexed notable hash database at ./input/notablehashes.txt-md5.idx # In addition, any keywords to search for must be in ./input/notablekeywords.xml -# Options: +# When the -l flag is set, the script looks for a config.xml file of the given name +# where images are stored. For usage notes please see the example "config.xml" in +# the /script folder. +# Options: # -r, --rebuild Rebuild the gold standards from the test results for each image -# -u, --ignore +# -i, --ignore Ignores unallocated space when ingesting. Faster, but less accurate results. + hadErrors = False # If any of the tests failed results = {} # Dictionary in which to store map ({imgname}->errors) @@ -28,14 +35,18 @@ outDir = os.path.join("output",time.strftime("%Y.%m.%d-%H.%M")) # Run ingest on all the images in 'input', using notablekeywords.xml and notablehashes.txt-md5.idx -def testAddImageIngest(inFile, ignoreUnalloc): +def testAddImageIngest(inFile, ignoreUnalloc, list): print "================================================" print "Ingesting Image: " + inFile # Set up case directory path testCaseName = imageName(inFile) + + #check for flags to append to folder name if ignoreUnalloc: - testCaseName+="-u" + testCaseName+="-i" + if list: + testCaseName+="-l" if os.path.exists(os.path.join(outDir,testCaseName)): shutil.rmtree(os.path.join(outDir,testCaseName)) os.makedirs(os.path.join(outDir,testCaseName)) @@ -44,20 +55,27 @@ def testAddImageIngest(inFile, ignoreUnalloc): cwd = wgetcwd() testInFile = wabspath(inFile) + # NEEDS windows path (backslashes) for .E00 images to work testInFile = testInFile.replace("/", "\\") - knownBadPath = os.path.join(cwd,inDir,"notablehashes.txt-md5.idx") + if list: + knownBadPath = os.path.join(inDir, "notablehashes.txt-md5.idx") + keywordPath = os.path.join(inDir, "notablekeywords.xml") + nsrlPath = os.path.join(inDir, "nsrl.txt-md5.idx") + else: + knownBadPath = os.path.join(cwd,inDir,"notablehashes.txt-md5.idx") + keywordPath = os.path.join(cwd,inDir,"notablekeywords.xml") + nsrlPath = os.path.join(cwd,inDir,"nsrl.txt-md5.idx") + knownBadPath = knownBadPath.replace("/", "\\") - keywordPath = os.path.join(cwd,inDir,"notablekeywords.xml") keywordPath = keywordPath.replace("/", "\\") - nsrlPath = os.path.join(cwd,inDir,"nsrl.txt-md5.idx") nsrlPath = nsrlPath.replace("/", "\\") antlog = os.path.join(cwd,outDir,testCaseName,"antlog.txt") antlog = antlog.replace("/", "\\") timeout = 24 * 60 * 60 * 1000 # default of 24 hours, just to be safe - size = getImageSize(inFile) # get the size in bytes + size = getImageSize(inFile, list) # get the size in bytes timeout = (size / 1000) / 1000 # convert to MB timeout = timeout * 1000 # convert sec to ms timeout = timeout * 1.5 # add a little extra umph @@ -80,7 +98,6 @@ def testAddImageIngest(inFile, ignoreUnalloc): args.append("-Dignore_unalloc=" + "%s" % ignoreUnalloc) args.append("-Dtest.timeout=" + str(timeout)) - # print the ant testing command print "CMD: " + " ".join(args) @@ -90,17 +107,21 @@ def testAddImageIngest(inFile, ignoreUnalloc): #fnull.close(); subprocess.call(args) -def getImageSize(inFile): +def getImageSize(inFile, list): name = imageName(inFile) - path = os.path.join(".",inDir) size = 0 - for files in os.listdir(path): - filename = os.path.splitext(files)[0] - if filename == name: - filepath = os.path.join(path, files) - if not os.path.samefile(filepath, inFile): - size += os.path.getsize(filepath) - size += os.path.getsize(inFile) + if list: + size += os.path.getsize(inFile) + else: + path = os.path.join(".",inDir) + + for files in os.listdir(path): + filename = os.path.splitext(files)[0] + if filename == name: + filepath = os.path.join(path, files) + if not os.path.samefile(filepath, inFile): + size += os.path.getsize(filepath) + size += os.path.getsize(inFile) return size def testCompareToGold(inFile, ignore): @@ -109,7 +130,7 @@ def testCompareToGold(inFile, ignore): name = imageName(inFile) if ignore: - name += ("-u") + name += ("-i") cwd = wgetcwd() goldFile = os.path.join("./",goldDir,name,"standard.db") testFile = os.path.join("./",outDir,name,"AutopsyTestCase","autopsy.db") @@ -166,32 +187,38 @@ def testCompareToGold(inFile, ignore): else: print("Object counts match!") -def clearGoldDir(inFile, ignore): +def clearGoldDir(inFile, ignore, list): cwd = wgetcwd() inFile = imageName(inFile) if ignore: - inFile += "-u" + inFile += "-i" + if list: + inFile += "-l" if os.path.exists(os.path.join(cwd,goldDir,inFile)): shutil.rmtree(os.path.join(cwd,goldDir,inFile)) os.makedirs(os.path.join(cwd,goldDir,inFile)) -def copyTestToGold(inFile, ignore): +def copyTestToGold(inFile, ignore, list): print "------------------------------------------------" print "Recreating gold standard from results." inFile = imageName(inFile) if ignore: - inFile += "-u" + inFile += "-i" + if list: + inFile += "-l" cwd = wgetcwd() goldFile = os.path.join("./",goldDir,inFile,"standard.db") testFile = os.path.join("./",outDir,inFile,"AutopsyTestCase","autopsy.db") shutil.copy(testFile, goldFile) -def copyReportToGold(inFile, ignore): +def copyReportToGold(inFile, ignore, list): print "------------------------------------------------" print "Recreating gold report from results." inFile = imageName(inFile) if ignore: - inFile += "-u" + inFile += "-i" + if list: + inFile += "-l" cwd = wgetcwd() goldReport = os.path.join("./",goldDir,inFile,"report.html") testReportPath = os.path.join("./",outDir,inFile,"AutopsyTestCase","Reports") @@ -208,12 +235,14 @@ def copyReportToGold(inFile, ignore): else: shutil.copy(testReport, goldReport) -def testCompareReports(inFile, ignore): +def testCompareReports(inFile, ignore, list): print "------------------------------------------------" print "Comparing report to golden report." name = imageName(inFile) if ignore: - name += "-u" + name += "-i" + if list: + name += "-l" goldReport = os.path.join("./",goldDir,name,"report.html") testReportPath = os.path.join("./",outDir,name,"AutopsyTestCase","Reports") # Because Java adds a timestamp to the report file, one can't call it @@ -276,15 +305,20 @@ def imageType(inFile): def imageName(inFile): pathEnd = inFile.rfind("/") + pathEnd2 = inFile.rfind("\\") extStart = inFile.rfind(".") if(extStart == -1 and extStart == -1): return inFile + if(pathEnd2 != -1): + return inFile[pathEnd2+1:extStart] elif(extStart == -1): return inFile[pathEnd+1:] elif(pathEnd == -1): return inFile[:extStart] - else: + elif(pathEnd!=-1 and extStart!=-1): return inFile[pathEnd+1:extStart] + else: + return inFile[pathEnd2+1:extStart] def markError(errString, inFile): global hadErrors @@ -300,41 +334,47 @@ def wgetcwd(): return out.rstrip() def wabspath(inFile): - proc = subprocess.Popen(("cygpath", "-m", os.path.abspath(inFile)), stdout=subprocess.PIPE) - out,err = proc.communicate() + if(inFile[1:2] == ":"): + proc = subprocess.Popen(("cygpath", "-m", inFile), stdout=subprocess.PIPE) + out,err = proc.communicate() + else: + proc = subprocess.Popen(("cygpath", "-m", os.path.abspath(inFile)), stdout=subprocess.PIPE) + out,err = proc.communicate() return out.rstrip() -def copyLogs(inFile, ignore): +def copyLogs(inFile, ignore, list): name = imageName(inFile) if ignore: - name +="-u" + name +="-i" + if list: + name+="-l" logDir = os.path.join("..","build","test","qa-functional","work","userdir0","var","log") shutil.copytree(logDir,os.path.join(outDir,name,"logs")) -def testFile(image, rebuild, ignore): +def testFile(image, rebuild, ignore, list): if imageType(image) != ImgType.UNKNOWN: - if ignore: - testAddImageIngest(image, True) - else: - testAddImageIngest(image, False) - copyLogs(image, ignore) + testAddImageIngest(image, ignore, list) + copyLogs(image, ignore, list) if rebuild: - clearGoldDir(image, ignore) - copyTestToGold(image, ignore) - copyReportToGold(image, ignore) + clearGoldDir(image, ignore, list) + copyTestToGold(image, ignore, list) + copyReportToGold(image, ignore, list) else: - testCompareToGold(image, ignore) - testCompareReports(image, ignore) - -def usage() : + testCompareToGold(image, ignore, list) + testCompareReports(image, ignore, list) + +def usage(): usage = "\ - Usage: ./regression.py [-i FILE] [OPTIONS] \n\n\ + Usage: ./regression.py [-s FILE] [OPTIONS] \n\n\ Run the RegressionTest.java file, and compare the result with a gold standard \n\n\ When the -i flag is set, this script only tests the image given by FILE.\n\ By default, it tests every image in ./input/\n\n\ An indexed NSRL database is expected at ./input/nsrl.txt-md5.idx,\n\ and an indexed notable hash database at ./input/notablehashes.txt-md5.idx\n\ In addition, any keywords to search for must be in ./input/notablekeywords.xml\n\n\ + When the -l flag is set, the script looks for a config.xml file of the given name\n\ + where images are stored. For usage notes please see the example config.xml in\n\ + the /script folder.\ Options:\n\n\ -r, --rebuild\t\tRebuild the gold standards from the test results for each image\n\n\ -u, --ignore\t\tIgnore unallocated space while ingesting" @@ -344,19 +384,36 @@ def main(): rebuild = False single = False ignore = False + list = False test = True argi = 1 + Config = None #file pointed to by --list + imgListB = [] #list of legal images from config + cwd = wgetcwd() while argi < len(sys.argv): arg = sys.argv[argi] - if arg == "-i" and argi+1 < len(sys.argv): + if arg == "-s" and argi+1 < len(sys.argv): #check for single single = True argi+=1 image = sys.argv[argi] print "Running on single image: " + image - elif (arg == "--rebuild") or (arg == "-r"): + if arg == "-l" or arg == "--list": #check for config file + list = True + argi+=1 + #check for file in ./ + if(os.path.isfile(os.path.join("./", sys.argv[argi]))): + Config = parse(os.path.join("./", sys.argv[argi])) + #else check if it is a specified path + elif (os.path.exists(wabspath(sys.argv[argi]))): + Config = parse(sys.argv[argi]) + else: + print sys.argv[argi] + print wabspath(sys.argv[argi]) + markError("Ran with " + arg +" but no such file exists", arg) + elif (arg == "--rebuild") or (arg == "-r"): #check for rebuild flag rebuild = True print "Running in REBUILD mode" - elif (arg == "--ignore") or (arg == "-u"): + elif (arg == "--ignore") or (arg == "-i"): #check for ignore flag ignore = True print "Ignoring unallocated space" else: @@ -365,9 +422,28 @@ def main(): argi+=1 if single: testFile(image, rebuild, ignore) + if list: + listImages = [] + errors = 0 + global inDir + out = Config.getElementsByTagName("indir")[0].getAttribute("value").encode() #there should only be one indir element in the config + inDir = out + for element in Config.getElementsByTagName("image"): + elem = element.getAttribute("value").encode() + proc2 = subprocess.Popen(("cygpath", "-i", elem), stdout=subprocess.PIPE) + out2,err = proc2.communicate() + out2 = out2.rstrip() + if(os.path.exists(out2) and os.path.isfile(out2)): + listImages.append(elem) + else: + print out2 + " is not a valid path or is not an image" + errors+=1 + print "Illegal files specified: " + str(errors) + for image in listImages: + testFile(image, rebuild, ignore, list) elif test: for inFile in os.listdir(inDir): - testFile(os.path.join(inDir,inFile), rebuild, ignore) + testFile(os.path.join(inDir,inFile), rebuild, ignore, list) if hadErrors == True: print "**********************************************" From cc5b9f4eed8490a6ca25e94a47a6814314e54b69 Mon Sep 17 00:00:00 2001 From: dhurd Date: Thu, 19 Jul 2012 13:50:20 -0400 Subject: [PATCH 2/2] Moved file/dir extraction into it's own thread and added a progress bar for said extraction. --- DataModel/nbproject/project.xml | 9 + .../autopsy/datamodel/ContentUtils.java | 55 ++++++- DirectoryTree/nbproject/project.xml | 9 + .../autopsy/directorytree/ExtractAction.java | 155 ++++++++++++++++-- 4 files changed, 209 insertions(+), 19 deletions(-) diff --git a/DataModel/nbproject/project.xml b/DataModel/nbproject/project.xml index f0b7259357..c57866e184 100644 --- a/DataModel/nbproject/project.xml +++ b/DataModel/nbproject/project.xml @@ -6,6 +6,15 @@ org.sleuthkit.autopsy.datamodel + + org.netbeans.api.progress + + + + 1 + 1.28.1 + + org.openide.awt diff --git a/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java b/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java index 565e2348c6..2fb43af3fb 100644 --- a/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java +++ b/DataModel/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java @@ -28,6 +28,8 @@ import java.util.List; import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.Directory; @@ -214,25 +216,47 @@ public final class ContentUtils { * it does * @throws IOException */ - public static void writeToFile(Content content, java.io.File outputFile) throws IOException { + public static void writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, SwingWorker worker, boolean source) throws IOException { InputStream in = new ReadContentInputStream(content); boolean append = false; FileOutputStream out = new FileOutputStream(outputFile, append); + + // Get the unit size for a progress bar + int unit = (int) (content.getSize() / 100); + long totalRead = 0; try { byte[] buffer = new byte[TO_FILE_BUFFER_SIZE]; int len = in.read(buffer); while (len != -1) { + // If there is a worker, check for a cancelation + if (worker!=null && worker.isCancelled()) { + break; + } out.write(buffer, 0, len); len = in.read(buffer); + totalRead+=len; + // If there is a progress bar and this is the source file, + // report any progress + if(progress!=null && source) { + int totalProgress = (int) (totalRead / unit); + progress.progress(content.getName(), totalProgress); + // If it's not the source, just update the file being processed + } else if(progress!=null && !source) { + progress.progress(content.getName()); + } } } finally { out.close(); } } + public static void writeToFile(Content content, java.io.File outputFile) throws IOException { + writeToFile(content, outputFile, null, null, false); + } + /** * Helper to ignore the '.' and '..' directories */ @@ -250,11 +274,21 @@ public final class ContentUtils { public static class ExtractFscContentVisitor extends ContentVisitor.Default { java.io.File dest; + ProgressHandle progress; + SwingWorker worker; + boolean source = false; /** * Make new extractor for a specific destination * @param dest The file/folder visited will be extracted as this file */ + public ExtractFscContentVisitor(java.io.File dest, ProgressHandle progress, SwingWorker worker, boolean source) { + this.dest = dest; + this.progress = progress; + this.worker = worker; + this.source = source; + } + public ExtractFscContentVisitor(java.io.File dest) { this.dest = dest; } @@ -263,13 +297,13 @@ public final class ContentUtils { * Convenience method to make a new instance for given destination * and extract given content */ - public static void extract(Content cntnt, java.io.File dest) { - cntnt.accept(new ExtractFscContentVisitor(dest)); + public static void extract(Content cntnt, java.io.File dest, ProgressHandle progress, SwingWorker worker) { + cntnt.accept(new ExtractFscContentVisitor(dest, progress, worker, true)); } public Void visit(File f) { try { - ContentUtils.writeToFile(f, dest); + ContentUtils.writeToFile(f, dest, progress, worker, source); } catch (IOException ex) { logger.log(Level.SEVERE, "Trouble extracting file to " + dest.getAbsolutePath(), @@ -292,12 +326,23 @@ public final class ContentUtils { DestFileContentVisitor destFileCV = new DestFileContentVisitor(); try { + int numProcessed = 0; // recurse on children for (Content child : dir.getChildren()) { java.io.File childFile = child.accept(destFileCV); ExtractFscContentVisitor childVisitor = - new ExtractFscContentVisitor(childFile); + new ExtractFscContentVisitor(childFile, progress, worker, false); + // If this is the source directory of an extract it + // will have a progress and worker, and will keep track + // of the progress bar's progress + if(worker!=null && worker.isCancelled()) { + break; + } + if(progress!=null && source) { + progress.progress(child.getName(), numProcessed); + } child.accept(childVisitor); + numProcessed++; } } catch (TskException ex) { logger.log(Level.SEVERE, diff --git a/DirectoryTree/nbproject/project.xml b/DirectoryTree/nbproject/project.xml index e4b2710e0f..a0eea2fa8b 100644 --- a/DirectoryTree/nbproject/project.xml +++ b/DirectoryTree/nbproject/project.xml @@ -6,6 +6,15 @@ org.sleuthkit.autopsy.directorytree + + org.netbeans.api.progress + + + + 1 + 1.28.1 + + org.netbeans.modules.settings diff --git a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 672720cd13..7b288fbe85 100644 --- a/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/DirectoryTree/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -18,17 +18,23 @@ */ package org.sleuthkit.autopsy.directorytree; -import java.awt.event.ActionEvent; -import javax.swing.JFileChooser; -import java.io.File; import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.AbstractAction; +import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.nodes.Node; +import org.openide.util.Cancellable; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; -import org.sleuthkit.autopsy.coreutils.Log; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.Directory; @@ -41,6 +47,7 @@ public final class ExtractAction extends AbstractAction { private static final InitializeContentVisitor initializeCV = new InitializeContentVisitor(); private FsContent fsContent; + private Logger logger = Logger.getLogger(ExtractAction.class.getName()); public ExtractAction(String title, Node contentNode) { super(title); @@ -78,8 +85,7 @@ public final class ExtractAction extends AbstractAction { */ @Override public void actionPerformed(ActionEvent e) { - Log.noteAction(this.getClass()); - + // Get file and check that it's okay to overwrite existing file JFileChooser fc = new JFileChooser(); fc.setCurrentDirectory(new File(Case.getCurrentCase().getCaseDirectory())); fc.setSelectedFile(new File(this.fsContent.getName())); @@ -88,7 +94,7 @@ public final class ExtractAction extends AbstractAction { if (returnValue == JFileChooser.APPROVE_OPTION) { File destination = fc.getSelectedFile(); - // check that it's okay to overwrite existing file + // do the check if (destination.exists()) { int choice = JOptionPane.showConfirmDialog( (Component) e.getSource(), @@ -97,7 +103,7 @@ public final class ExtractAction extends AbstractAction { JOptionPane.OK_CANCEL_OPTION); if (choice != JOptionPane.OK_OPTION) { - return; + return; // Just exit the function } if (!destination.delete()) { @@ -106,13 +112,134 @@ public final class ExtractAction extends AbstractAction { "Couldn't delete existing file."); } } - - ExtractFscContentVisitor.extract(fsContent, destination); - if(fsContent.isDir()) - JOptionPane.showMessageDialog((Component) e.getSource(), "Directory extracted."); - else if(fsContent.isFile()){ - JOptionPane.showMessageDialog((Component) e.getSource(), "File extracted."); + + try { + ExtractFileThread extract = new ExtractFileThread(); + extract.init(this.fsContent, e, destination); + extract.execute(); + } catch (Exception ex) { + logger.log(Level.WARNING, "Unable to start background thread.", ex); } } } + + private class ExtractFileThread extends SwingWorker { + private Logger logger = Logger.getLogger(ExtractFileThread.class.getName()); + private ProgressHandle progress; + private FsContent fsContent; + ActionEvent e; + File destination; + + private void init(FsContent fsContent, ActionEvent e, File destination) { + this.fsContent = fsContent; + this.e = e; + this.destination = destination; + } + + @Override + protected Object doInBackground() throws Exception { + logger.log(Level.INFO, "Starting background processing for file extraction."); + + // Setup progress bar + final String displayName = "Extracting"; + progress = ProgressHandleFactory.createHandle(displayName, new Cancellable() { + @Override + public boolean cancel() { + if (progress != null) + progress.setDisplayName(displayName + " (Cancelling...)"); + return ExtractAction.ExtractFileThread.this.cancel(true); + } + }); + + // Start the progress bar as indeterminate + progress.start(); + progress.switchToIndeterminate(); + if(fsContent.isFile()) { + // Max file size of 200GB + long filesize = fsContent.getSize(); + int unit = (int) (filesize / 100); + progress.switchToDeterminate(100); + } else if(fsContent.isDir()) { + // If dir base progress off number of children + int toProcess = fsContent.getChildren().size(); + progress.switchToDeterminate(toProcess); + } + + // Start extracting the file/directory + ExtractFscContentVisitor.extract(fsContent, destination, progress, this); + logger.log(Level.INFO, "Done background processing"); + return null; + } + + @Override + protected void done() { + try { + super.get(); //block and get all exceptions thrown while doInBackground() + } catch (CancellationException ex) { + logger.log(Level.INFO, "Extraction was canceled."); + } catch (InterruptedException ex) { + logger.log(Level.INFO, "Extraction was interrupted."); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Fatal error during file extraction.", ex); + } finally { + progress.finish(); + if (!this.isCancelled()) { + logger.log(Level.INFO, "Extracting completed without cancellation."); + // Alert the user extraction is over + if(fsContent.isDir()) { + JOptionPane.showMessageDialog((Component) e.getSource(), "Directory extracted."); + } else if(fsContent.isFile()){ + JOptionPane.showMessageDialog((Component) e.getSource(), "File extracted."); + } + } else { + logger.log(Level.INFO, "Attempting to delete file(s)."); + if(delete(destination)) { + logger.log(Level.INFO, "Finished deletion sucessfully."); + } else { + logger.log(Level.WARNING, "Deletion attempt complete; not all files were sucessfully deleted."); + } + } + } + } + + private boolean delete(File file) { + boolean sucess = true; + // If it's a file + if(file.isFile()) { + if(!file.delete()) { + sucess = false; + logger.log(Level.WARNING, "Failed to delete file {0}", file.getPath()); + } + // If it's a directory + } else { + // If the dir is empty + if(file.list().length==0) { + if(!file.delete()) { + sucess = false; + logger.log(Level.WARNING, "Failed to delete the empty directory at {0}", file.getPath()); + } + } else { + String files[] = file.list(); + for(String s:files) { + File sub = new File(file, s); + sucess = delete(sub); + } + + // Delete the newly-empty dir + if(file.list().length==0) { + if(!file.delete()) { + sucess = false; + logger.log(Level.WARNING, "Failed to delete the empty directory at {0}", file.getPath()); + } + } else { + sucess = false; + logger.log(Level.WARNING, "Directory {0} did not recursivly delete sucessfully.", file.getPath()); + } + } + } + return sucess; + } + + } + }