20 package org.sleuthkit.autopsy.recentactivity;
 
   23 import java.io.FileOutputStream;
 
   24 import java.io.IOException;
 
   25 import java.io.OutputStream;
 
   26 import java.nio.file.Paths;
 
   27 import java.util.ArrayList;
 
   28 import java.util.List;
 
   29 import java.util.logging.Level;
 
   30 import org.apache.poi.EmptyFileException;
 
   31 import org.apache.poi.poifs.filesystem.DirectoryEntry;
 
   32 import org.apache.poi.poifs.filesystem.DocumentEntry;
 
   33 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 
   34 import org.apache.poi.poifs.filesystem.Entry;
 
   35 import org.apache.poi.poifs.filesystem.NotOLE2FileException;
 
   36 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
   37 import org.openide.util.NbBundle.Messages;
 
   60 final class ExtractJumpLists 
extends Extract {
 
   62     private static final Logger logger = Logger.getLogger(ExtractJumpLists.class.getName());
 
   63     private static final String RA_DIR_NAME = 
"RecentActivity"; 
 
   64     private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = 
"%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
 
   65     private static final String JUMPLIST_DIR_NAME = 
"jumplists"; 
 
   66     private static final String VERSION_NUMBER = 
"1.0.0"; 
 
   67     private String moduleName;
 
   68     private FileManager fileManager;
 
   69     private final IngestServices services = IngestServices.getInstance();
 
   70     private final IngestJobContext context;
 
   73         "Jumplist_module_name=Windows Jumplist Analyzer",
 
   74         "Jumplist_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis." 
   76     ExtractJumpLists(IngestJobContext context) {
 
   77         super(Bundle.Jumplist_module_name(), context);
 
   78         this.context = context;
 
   82     void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
 
   83         moduleName = Bundle.Jumplist_module_name();
 
   84         fileManager = currentCase.getServices().getFileManager();
 
   85         long ingestJobId = context.getJobId();
 
   87         String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
 
   88         List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId, baseRaTempPath);
 
   89         if (jumpListFiles.isEmpty()) {
 
   93         if (context.dataSourceIngestIsCancelled()) {
 
   97         List<AbstractFile> derivedFiles = 
new ArrayList<>();
 
   98         String derivedPath = null;
 
   99         String baseRaModPath = RAImageIngestModule.getRAOutputPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
 
  100         for (AbstractFile jumplistFile : jumpListFiles) {
 
  101             if (!jumplistFile.getName().toLowerCase().contains(
"-slack") && !jumplistFile.getName().equals(
"..")
 
  102                     && !jumplistFile.getName().equals(
".") && jumplistFile.getSize() > 0) {
 
  103                 String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() + 
"_" + jumplistFile.getId()).toString();
 
  104                 String moduleOutPath = baseRaModPath + File.separator + jumplistFile.getName() + 
"_" + jumplistFile.getId();
 
  105                 derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + 
"_" + ingestJobId + File.separator + jumplistFile.getName() + 
"_" + jumplistFile.getId();
 
  106                 File jlDir = 
new File(moduleOutPath);
 
  107                 if (jlDir.exists() == 
false) {
 
  108                     boolean dirMade = jlDir.mkdirs();
 
  110                         logger.log(Level.WARNING, 
"Error creating directory to store Jumplist LNK files %s", moduleOutPath); 
 
  114                 derivedFiles.addAll(extractLnkFiles(jlFile, moduleOutPath, jumplistFile, derivedPath));
 
  119         progressBar.progress(String.format(Bundle.Jumplist_adding_extracted_files_msg(), derivedFiles.size()));
 
  120         derivedFiles.forEach((derived) -> {
 
  121             services.fireModuleContentEvent(
new ModuleContentEvent(derived));
 
  123         context.addFilesToJob(derivedFiles);
 
  132     private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId, String baseRaTempPath) {
 
  133         List<AbstractFile> jumpListFiles = 
new ArrayList<>();;
 
  134         List<AbstractFile> tempJumpListFiles = 
new ArrayList<>();;
 
  136         FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
 
  139             tempJumpListFiles = fileManager.findFiles(dataSource, 
"%", AUTOMATIC_DESTINATIONS_FILE_DIRECTORY); 
 
  140             if (!tempJumpListFiles.isEmpty()) {
 
  141                 jumpListFiles.addAll(tempJumpListFiles);
 
  143         } 
catch (TskCoreException ex) {
 
  144             logger.log(Level.WARNING, 
"Unable to find jumplist files.", ex); 
 
  145             return jumpListFiles;  
 
  148         for (AbstractFile jumpListFile : jumpListFiles) {
 
  150             if (context.dataSourceIngestIsCancelled()) {
 
  151                 return jumpListFiles;
 
  154             if (!jumpListFile.getName().toLowerCase().contains(
"-slack") && !jumpListFile.getName().equals(
"..")
 
  155                     && !jumpListFile.getName().equals(
".") && jumpListFile.getSize() > 0) {
 
  156                 String fileName = jumpListFile.getName() + 
"_" + jumpListFile.getId();
 
  157                 String jlFile = Paths.get(baseRaTempPath, fileName).toString();
 
  159                     ContentUtils.writeToFile(jumpListFile, 
new File(jlFile));
 
  160                 } 
catch (IOException ex) {
 
  161                     logger.log(Level.WARNING, String.format(
"Unable to write %s to temp directory. File name: %s", fileName, jlFile), ex); 
 
  166         return jumpListFiles;
 
  173     private List<DerivedFile> extractLnkFiles(String jumpListFile, String moduleOutPath, AbstractFile jumpListAbsFile, String derivedPath) {
 
  175         List<DerivedFile> derivedFiles = 
new ArrayList<>();
 
  176         DerivedFile derivedFile;
 
  177         String lnkFileName = 
"";
 
  179         try (POIFSFileSystem fs = 
new POIFSFileSystem(
new File(jumpListFile))) {
 
  180             DirectoryEntry root = fs.getRoot();
 
  181             for (Entry entry : root) {
 
  182                 if (entry instanceof DirectoryEntry) {
 
  185                 } 
else if (entry instanceof DocumentEntry) {
 
  186                     String jmpListFileName = entry.getName();
 
  187                     int fileSize = ((DocumentEntry) entry).getSize();
 
  190                         try (DocumentInputStream stream = fs.createDocumentInputStream(jmpListFileName)) {
 
  191                             byte[] buffer = 
new byte[stream.available()];
 
  194                             JLnkParser lnkParser = 
new JLnkParser(fs.createDocumentInputStream(jmpListFileName), fileSize);
 
  195                             JLNK lnk = lnkParser.parse();
 
  196                             lnkFileName = lnk.getBestName() + 
".lnk";
 
  197                             File targetFile = 
new File(moduleOutPath + File.separator + entry.getName() + 
"-" + lnkFileName);
 
  198                             String relativePath = Case.getCurrentCase().getModuleOutputDirectoryRelativePath();
 
  199                             String derivedFileName = Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator + derivedPath + File.separator + entry.getName() + 
"-" + lnkFileName;
 
  200                             OutputStream outStream = 
new FileOutputStream(targetFile);
 
  201                             outStream.write(buffer);
 
  203                             derivedFile = fileManager.addDerivedFile(lnkFileName, derivedFileName,
 
  215                                     TskData.EncodingType.NONE);
 
  216                             derivedFiles.add(derivedFile);
 
  218                         } 
catch (IOException | JLnkParserException ex) {
 
  219                             logger.log(Level.WARNING, String.format(
"No such document, or the Entry represented by documentName is not a DocumentEntry link file is %s", jumpListFile), ex); 
 
  220                         } 
catch (TskCoreException ex) {
 
  221                             logger.log(Level.WARNING, String.format(
"Error trying to add dervived file %s", lnkFileName), ex); 
 
  222                         } 
catch (IndexOutOfBoundsException ex) {
 
  225                             logger.log(Level.WARNING, String.format(
"Error parsing the the jumplist file %s", jumpListFile), ex); 
 
  235         } 
catch (NotOLE2FileException | EmptyFileException ex1) {
 
  236             logger.log(Level.WARNING, String.format(
"Error file not a valid OLE2 Document $s", jumpListFile)); 
 
  237         } 
catch (IOException ex) {
 
  238             logger.log(Level.WARNING, String.format(
"Error lnk parsing the file to get recent files $s", jumpListFile), ex);