<commit_before>/*
 *
 * Autopsy Forensic Browser
 *
 * Copyright 2020-2021 Basis Technology Corp.
 *
 *
 * 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.recentactivity;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
import static org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;

/**
 * Extract the Prefetch Files and process them thru an External program. The
 * data will then be added to the TSK_PROG_RUN artifact. Associated artifacts
 * will be created if possible.
 */
final class ExtractPrefetch extends Extract {

    private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());

    private IngestJobContext context;

    private static final String PREFETCH_TSK_COMMENT = "Prefetch File";
    private static final String PREFETCH_FILE_LOCATION = "/windows/prefetch";
    private static final String PREFETCH_TOOL_FOLDER = "markmckinnon"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_WINDOWS_64 = "parse_prefetch_x64.exe"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_WINDOWS_32 = "parse_prefetch_x32.exe"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_MACOS = "parse_prefetch_macos"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_LINUX = "parse_prefetch_linux"; //NON-NLS
    private static final String PREFETCH_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
    private static final String PREFETCH_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
    private static final String PREFETCH_PARSER_DB_FILE = "Autopsy_PF_DB.db3"; //NON-NLS
    private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS

    @Messages({
        "ExtractPrefetch_module_name=Windows Prefetch Extractor",
        "# {0} - sub module name", 
        "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
    })
    ExtractPrefetch() {
        super(Bundle.ExtractPrefetch_module_name());
    }

    @Override
    void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {

        this.context = context;
        long ingestJobId = context.getJobId();

        String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + PREFETCH_DIR_NAME;
        File dir = new File(modOutPath);
        if (dir.exists() == false) {
            boolean dirMade = dir.mkdirs();
            if (!dirMade) {
                logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
                return; //If we cannot create the directory then we need to exit
            }
        }

        extractPrefetchFiles(dataSource, ingestJobId);

        final String prefetchDumper = getPathForPrefetchDumper();
        if (prefetchDumper == null) {
            logger.log(Level.SEVERE, "Error finding parse_prefetch program"); //NON-NLS
            return; //If we cannot find the parse_prefetch program we cannot proceed
        }

        if (context.dataSourceIngestIsCancelled()) {
            return;
        }

        String modOutFile = modOutPath + File.separator + dataSource.getId() + "-" + PREFETCH_PARSER_DB_FILE;
        try {
            String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getId() + "-" + PREFETCH_DIR_NAME, ingestJobId);
            parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
            File prefetchDatabase = new File(modOutFile);
            if (prefetchDatabase.exists()) {
                createAppExecArtifacts(modOutFile, dataSource);
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS 
            addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
        }
    }

    /**
     * Extract prefetch file to temp directory to process. Checks to make sure
     * that the prefetch files only come from the /Windows/Prefetch directory
     *
     * @param dataSource - datasource to search for prefetch files
     */
    void extractPrefetchFiles(Content dataSource, long ingestJobId) {
        List<AbstractFile> pFiles;

        FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();

        try {
            pFiles = fileManager.findFiles(dataSource, "%.pf"); //NON-NLS            
        } catch (TskCoreException ex) {
            logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
            return;  // No need to continue
        }

        for (AbstractFile pFile : pFiles) {

            if (context.dataSourceIngestIsCancelled()) {
                return;
            }

            if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
                String origFileName = pFile.getName();
                String ext = FilenameUtils.getExtension(origFileName);
                String baseName = FilenameUtils.getBaseName(origFileName);
                String fileName = escapeFileName(String.format("%s_%d.%s", baseName, pFile.getId(), ext));
                String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), dataSource.getName() + "-" + PREFETCH_DIR_NAME, ingestJobId);
                String prefetchFile =  Paths.get(baseRaTempPath, fileName).toString();
                try {<commit_msg>Use consistent temp file name<commit_after>/*
 *
 * Autopsy Forensic Browser
 *
 * Copyright 2020-2021 Basis Technology Corp.
 *
 *
 * 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.recentactivity;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
import static org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;

/**
 * Extract the Prefetch Files and process them thru an External program. The
 * data will then be added to the TSK_PROG_RUN artifact. Associated artifacts
 * will be created if possible.
 */
final class ExtractPrefetch extends Extract {

    private static final Logger logger = Logger.getLogger(ExtractPrefetch.class.getName());

    private IngestJobContext context;

    private static final String PREFETCH_TSK_COMMENT = "Prefetch File";
    private static final String PREFETCH_FILE_LOCATION = "/windows/prefetch";
    private static final String PREFETCH_TOOL_FOLDER = "markmckinnon"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_WINDOWS_64 = "parse_prefetch_x64.exe"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_WINDOWS_32 = "parse_prefetch_x32.exe"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_MACOS = "parse_prefetch_macos"; //NON-NLS
    private static final String PREFETCH_TOOL_NAME_LINUX = "parse_prefetch_linux"; //NON-NLS
    private static final String PREFETCH_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
    private static final String PREFETCH_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
    private static final String PREFETCH_PARSER_DB_FILE = "Autopsy_PF_DB.db3"; //NON-NLS
    private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS

    @Messages({
        "ExtractPrefetch_module_name=Windows Prefetch Extractor",
        "# {0} - sub module name", 
        "ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
    })
    ExtractPrefetch() {
        super(Bundle.ExtractPrefetch_module_name());
    }

    /**
     * Get the temp folder name.
     * 
     * @param dataSource Current data source
     * @return The folder name
     */
    private String getPrefetchTempFolder(Content dataSource) {
        return dataSource.getId() + "-" + PREFETCH_PARSER_DB_FILE;
    }
    
    @Override
    void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {

        this.context = context;
        long ingestJobId = context.getJobId();

        String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + PREFETCH_DIR_NAME;
        File dir = new File(modOutPath);
        if (dir.exists() == false) {
            boolean dirMade = dir.mkdirs();
            if (!dirMade) {
                logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
                return; //If we cannot create the directory then we need to exit
            }
        }

        extractPrefetchFiles(dataSource, ingestJobId);

        final String prefetchDumper = getPathForPrefetchDumper();
        if (prefetchDumper == null) {
            logger.log(Level.SEVERE, "Error finding parse_prefetch program"); //NON-NLS
            return; //If we cannot find the parse_prefetch program we cannot proceed
        }

        if (context.dataSourceIngestIsCancelled()) {
            return;
        }

        String modOutFile = modOutPath + File.separator + getPrefetchTempFolder(dataSource);
        try {
            String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
            parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
            File prefetchDatabase = new File(modOutFile);
            if (prefetchDatabase.exists()) {
                createAppExecArtifacts(modOutFile, dataSource);
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS 
            addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
        }
    }

    /**
     * Extract prefetch file to temp directory to process. Checks to make sure
     * that the prefetch files only come from the /Windows/Prefetch directory
     *
     * @param dataSource - datasource to search for prefetch files
     */
    void extractPrefetchFiles(Content dataSource, long ingestJobId) {
        List<AbstractFile> pFiles;

        FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();

        try {
            pFiles = fileManager.findFiles(dataSource, "%.pf"); //NON-NLS            
        } catch (TskCoreException ex) {
            logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
            return;  // No need to continue
        }

        for (AbstractFile pFile : pFiles) {

            if (context.dataSourceIngestIsCancelled()) {
                return;
            }

            if (pFile.getParentPath().toLowerCase().contains(PREFETCH_FILE_LOCATION.toLowerCase()) && pFile.getSize() > 0) {
                String origFileName = pFile.getName();
                String ext = FilenameUtils.getExtension(origFileName);
                String baseName = FilenameUtils.getBaseName(origFileName);
                String fileName = escapeFileName(String.format("%s_%d.%s", baseName, pFile.getId(), ext));
                String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), getPrefetchTempFolder(dataSource), ingestJobId);
                String prefetchFile =  Paths.get(baseRaTempPath, fileName).toString();
                try {
{'fraction_non_alphanumeric': 0.09092127303182579, 'fraction_numerical': 0.0032160804020100503, 'mean_word_length': 3.4016514302565617, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7061950', 'n_tokens_mistral': 4470, 'n_tokens_neox': 4311, 'n_words': 1048}