Package org.sleuthkit.autopsy.thunderbirdparser

Source Code of org.sleuthkit.autopsy.thunderbirdparser.PstParser

/*
* Autopsy Forensic Browser
*
* Copyright 2011-2014 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.thunderbirdparser;

import com.pff.PSTAttachment;
import com.pff.PSTException;
import com.pff.PSTFile;
import com.pff.PSTFolder;
import com.pff.PSTMessage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.ingest.IngestServices;
import static org.sleuthkit.autopsy.thunderbirdparser.ThunderbirdMboxFileIngestModule.getRelModuleOutputPath;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;

/**
* Parser for extracting emails from pst/ost Mircosoft Outlook data files.
*
* @author jwallace
*/
class PstParser {

    private static final Logger logger = Logger.getLogger(PstParser.class.getName());
    /**
     * First four bytes of a pst file.
     */
    private static int PST_HEADER = 0x2142444E;
    private IngestServices services;
    /**
     * A map of PSTMessages to their Local path within the file's internal
     * directory structure.
     */
    private List<EmailMessage> results;
    private StringBuilder errors;

    PstParser(IngestServices services) {
        results = new ArrayList<>();
        this.services = services;
        errors = new StringBuilder();
    }

    enum ParseResult {

        OK, ERROR, ENCRYPT;
    }

    /**
     * Parse and extract email messages from the pst/ost file.
     *
     * @param file A pst or ost file.
     * @return ParseResult: OK on success, ERROR on an error, ENCRYPT if failed
     * because the file is encrypted.
     */
    ParseResult parse(File file) {
        PSTFile pstFile;
        long failures;
        try {
            pstFile = new PSTFile(file);
            failures = processFolder(pstFile.getRootFolder(), "\\", true);
            if (failures > 0) {
                addErrorMessage(
                        NbBundle.getMessage(this.getClass(), "PstParser.parse.errMsg.failedToParseNMsgs", failures));
            }
            return ParseResult.OK;
        } catch (PSTException | IOException ex) {
            String msg = file.getName() + ": Failed to create internal java-libpst PST file to parse:\n" + ex.getMessage(); //NON-NLS
            logger.log(Level.WARNING, msg);
            return ParseResult.ERROR;
        } catch (IllegalArgumentException ex) {
            logger.log(Level.INFO, "Found encrypted PST file."); //NON-NLS
            return ParseResult.ENCRYPT;
        }
    }

    /**
     * Get the results of the parsing.
     *
     * @return
     */
    List<EmailMessage> getResults() {
        return results;
    }

    String getErrors() {
        return errors.toString();
    }

    /**
     * Process this folder and all subfolders, adding every email found to
     * results. Accumulates the folder hierarchy path as it navigates the folder
     * structure.
     *
     * @param folder The folder to navigate and process
     * @param path The path to the folder within the pst/ost file's directory
     * structure
     * @throws PSTException
     * @throws IOException
     */
    private long processFolder(PSTFolder folder, String path, boolean root) {
        String newPath = (root ? path : path + "\\" + folder.getDisplayName());
        long failCount = 0L; // Number of emails that failed
        if (folder.hasSubfolders()) {
            List<PSTFolder> subFolders;
            try {
                subFolders = folder.getSubFolders();
            } catch (PSTException | IOException ex) {
                subFolders = new ArrayList<>();
                logger.log(Level.INFO, "java-libpst exception while getting subfolders: {0}", ex.getMessage()); //NON-NLS
            }

            for (PSTFolder f : subFolders) {
                failCount += processFolder(f, newPath, false);
            }
        }

        if (folder.getContentCount() != 0) {
            PSTMessage email;
            // A folder's children are always emails, never other folders.
            try {
                while ((email = (PSTMessage) folder.getNextChild()) != null) {
                    results.add(extractEmailMessage(email, newPath));
                }
            } catch (PSTException | IOException ex) {
                failCount++;
                logger.log(Level.INFO, "java-libpst exception while getting emails from a folder: {0}", ex.getMessage()); //NON-NLS
            }
        }

        return failCount;
    }

    /**
     * Create an EmailMessage from a PSTMessage.
     *
     * @param msg
     * @param localPath
     * @return
     */
    private EmailMessage extractEmailMessage(PSTMessage msg, String localPath) {
        EmailMessage email = new EmailMessage();
        email.setRecipients(msg.getDisplayTo());
        email.setCc(msg.getDisplayCC());
        email.setBcc(msg.getDisplayBCC());
        email.setSender(getSender(msg.getSenderName(), msg.getSenderEmailAddress()));
        email.setSentDate(msg.getMessageDeliveryTime());
        email.setTextBody(msg.getBody());
        email.setHtmlBody(msg.getBodyHTML());
        String rtf = "";
        try {
            rtf = msg.getRTFBody();
        } catch (PSTException | IOException ex) {
            logger.log(Level.INFO, "Failed to get RTF content from pst email."); //NON-NLS
        }
        email.setRtfBody(rtf);
        email.setLocalPath(localPath);
        email.setSubject(msg.getSubject());
        email.setId(msg.getDescriptorNodeId());

        if (msg.hasAttachments()) {
            extractAttachments(email, msg);
        }

        return email;
    }

    /**
     * Add the attachments within the PSTMessage to the EmailMessage.
     *
     * @param email
     * @param msg
     */
    private void extractAttachments(EmailMessage email, PSTMessage msg) {
        int numberOfAttachments = msg.getNumberOfAttachments();
        String outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator;
        for (int x = 0; x < numberOfAttachments; x++) {
            String filename = "";
            try {
                PSTAttachment attach = msg.getAttachment(x);
                long size = attach.getAttachSize();
                if (size >= services.getFreeDiskSpace()) {
                    continue;
                }
                // both long and short filenames can be used for attachments
                filename = attach.getLongFilename();
                if (filename.isEmpty()) {
                    filename = attach.getFilename();
                }
                String uniqueFilename = msg.getDescriptorNodeId() + "-" + filename;
                String outPath = outputDirPath + uniqueFilename;
                saveAttachmentToDisk(attach, outPath);

                EmailMessage.Attachment attachment = new EmailMessage.Attachment();

                long crTime = attach.getCreationTime().getTime() / 1000;
                long mTime = attach.getModificationTime().getTime() / 1000;
                String relPath = getRelModuleOutputPath() + File.separator + uniqueFilename;
                attachment.setName(filename);
                attachment.setCrTime(crTime);
                attachment.setmTime(mTime);
                attachment.setLocalPath(relPath);
                attachment.setSize(attach.getFilesize());
                email.addAttachment(attachment);
            } catch (PSTException | IOException ex) {
                addErrorMessage(
                        NbBundle.getMessage(this.getClass(), "PstParser.extractAttch.errMsg.failedToExtractToDisk",
                        filename));
                logger.log(Level.WARNING, "Failed to extract attachment from pst file.", ex); //NON-NLS
            }
        }
    }

    /**
     * Extracts a PSTAttachment to the module output directory.
     *
     * @param attach
     * @param outPath
     * @return
     * @throws IOException
     * @throws PSTException
     */
    private void saveAttachmentToDisk(PSTAttachment attach, String outPath) throws IOException, PSTException {
        try (InputStream attachmentStream = attach.getFileInputStream(); FileOutputStream out = new FileOutputStream(outPath)) {
            // 8176 is the block size used internally and should give the best performance
            int bufferSize = 8176;
            byte[] buffer = new byte[bufferSize];
            int count = attachmentStream.read(buffer);
            while (count == bufferSize) {
                out.write(buffer);
                count = attachmentStream.read(buffer);
            }
            byte[] endBuffer = new byte[count];
            System.arraycopy(buffer, 0, endBuffer, 0, count);
            out.write(endBuffer);
        }
    }

    /**
     * Pretty-Print "From" field of an outlook email message.
     *
     * @param name Sender's Name
     * @param addr Sender's Email address
     * @return
     */
    private String getSender(String name, String addr) {
        if (name.isEmpty() && addr.isEmpty()) {
            return "";
        } else if (name.isEmpty()) {
            return addr;
        } else if (addr.isEmpty()) {
            return name;
        } else {
            return name + ": " + addr;
        }
    }

    /**
     * Identify a file as a pst/ost file by it's header.
     *
     * @param file
     * @return
     */
    public static boolean isPstFile(AbstractFile file) {
        byte[] buffer = new byte[4];
        try {
            int read = file.read(buffer, 0, 4);
            if (read != 4) {
                return false;
            }
            ByteBuffer bb = ByteBuffer.wrap(buffer);
            return bb.getInt() == PST_HEADER;
        } catch (TskCoreException ex) {
            logger.log(Level.WARNING, "Exception while detecting if a file is a pst file."); //NON-NLS
            return false;
        }
    }

    private void addErrorMessage(String msg) {
        errors.append("<li>").append(msg).append("</li>"); //NON-NLS
    }
}
TOP

Related Classes of org.sleuthkit.autopsy.thunderbirdparser.PstParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.