19 package org.sleuthkit.autopsy.recentactivity;
 
   21 import com.dd.plist.NSArray;
 
   22 import com.dd.plist.NSDate;
 
   23 import com.dd.plist.NSDictionary;
 
   24 import com.dd.plist.NSObject;
 
   25 import com.dd.plist.NSString;
 
   26 import com.dd.plist.PropertyListFormatException;
 
   27 import com.dd.plist.PropertyListParser;
 
   29 import java.io.IOException;
 
   30 import java.nio.file.Path;
 
   31 import java.text.ParseException;
 
   32 import java.util.ArrayList;
 
   33 import java.util.Collection;
 
   34 import java.util.HashMap;
 
   35 import java.util.Iterator;
 
   36 import java.util.List;
 
   37 import java.util.logging.Level;
 
   38 import javax.xml.parsers.ParserConfigurationException;
 
   39 import org.apache.commons.io.FilenameUtils;
 
   40 import org.openide.util.NbBundle.Messages;
 
   55 import org.xml.sax.SAXException;
 
   61 final class ExtractSafari 
extends Extract {
 
   63     private final IngestServices services = IngestServices.getInstance();
 
   66     private static final String HISTORY_QUERY = 
"SELECT url, title, visit_time + 978307200 as time FROM 'history_items' JOIN history_visits ON history_item = history_items.id;"; 
 
   68     private static final String HISTORY_FILE_NAME = 
"History.db"; 
 
   69     private static final String BOOKMARK_FILE_NAME = 
"Bookmarks.plist"; 
 
   70     private static final String DOWNLOAD_FILE_NAME = 
"Downloads.plist"; 
 
   71     private static final String COOKIE_FILE_NAME = 
"Cookies.binarycookies"; 
 
   72     private static final String COOKIE_FOLDER = 
"Cookies";
 
   73     private static final String SAFARI_FOLDER = 
"Safari";
 
   75     private static final String HEAD_URL = 
"url"; 
 
   76     private static final String HEAD_TITLE = 
"title"; 
 
   77     private static final String HEAD_TIME = 
"time"; 
 
   79     private static final String PLIST_KEY_CHILDREN = 
"Children"; 
 
   80     private static final String PLIST_KEY_URL = 
"URLString"; 
 
   81     private static final String PLIST_KEY_URI = 
"URIDictionary"; 
 
   82     private static final String PLIST_KEY_TITLE = 
"title"; 
 
   83     private static final String PLIST_KEY_DOWNLOAD_URL = 
"DownloadEntryURL"; 
 
   84     private static final String PLIST_KEY_DOWNLOAD_DATE = 
"DownloadEntryDateAddedKey"; 
 
   85     private static final String PLIST_KEY_DOWNLOAD_PATH = 
"DownloadEntryPath"; 
 
   86     private static final String PLIST_KEY_DOWNLOAD_HISTORY = 
"DownloadHistory"; 
 
   88     private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName());
 
   91         "ExtractSafari_Module_Name=Safari",
 
   92         "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.",
 
   93         "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",
 
   94         "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files",
 
   95         "Progress_Message_Safari_History=Safari History",
 
   96         "Progress_Message_Safari_Bookmarks=Safari Bookmarks",
 
   97         "Progress_Message_Safari_Cookies=Safari Cookies",
 
   98         "Progress_Message_Safari_Downloads=Safari Downloads",
 
  110     protected String getName() {
 
  111         return Bundle.ExtractSafari_Module_Name();
 
  115     void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
 
  118         progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
 
  120             processHistoryDB(dataSource, context);
 
  122         } 
catch (IOException | TskCoreException ex) {
 
  123             this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History());
 
  124             LOG.log(Level.SEVERE, 
"Exception thrown while processing history file: {0}", ex); 
 
  127         progressBar.progress(Bundle.Progress_Message_Safari_Bookmarks());
 
  129             processBookmarkPList(dataSource, context);
 
  130         } 
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
 
  131             this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
 
  132             LOG.log(Level.SEVERE, 
"Exception thrown while parsing Safari Bookmarks file: {0}", ex); 
 
  135         progressBar.progress(Bundle.Progress_Message_Safari_Downloads());
 
  137             processDownloadsPList(dataSource, context);
 
  138         } 
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
 
  139             this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
 
  140             LOG.log(Level.SEVERE, 
"Exception thrown while parsing Safari Download.plist file: {0}", ex); 
 
  143         progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
 
  145             processBinaryCookieFile(dataSource, context);
 
  146         } 
catch (IOException | TskCoreException ex) {
 
  147             this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
 
  148             LOG.log(Level.SEVERE, 
"Exception thrown while processing Safari cookies file: {0}", ex); 
 
  159     private void processHistoryDB(Content dataSource, IngestJobContext context) 
throws TskCoreException, IOException {
 
  160         FileManager fileManager = getCurrentCase().getServices().getFileManager();
 
  162         List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER);
 
  164         if (historyFiles == null || historyFiles.isEmpty()) {
 
  170         for (AbstractFile historyFile : historyFiles) {
 
  171             if (context.dataSourceIngestIsCancelled()) {
 
  175             getHistory(context, historyFile);
 
  190     private void processBookmarkPList(Content dataSource, IngestJobContext context) 
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
 
  191         FileManager fileManager = getCurrentCase().getServices().getFileManager();
 
  193         List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER);
 
  195         if (files == null || files.isEmpty()) {
 
  201         for (AbstractFile file : files) {
 
  202             if (context.dataSourceIngestIsCancelled()) {
 
  206             getBookmarks(context, file);
 
  222     private void processDownloadsPList(Content dataSource, IngestJobContext context) 
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
 
  223         FileManager fileManager = getCurrentCase().getServices().getFileManager();
 
  225         List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER);
 
  227         if (files == null || files.isEmpty()) {
 
  233         for (AbstractFile file : files) {
 
  234             if (context.dataSourceIngestIsCancelled()) {
 
  238             getDownloads(dataSource, context, file);
 
  249     private void processBinaryCookieFile(Content dataSource, IngestJobContext context) 
throws TskCoreException, IOException {
 
  250         FileManager fileManager = getCurrentCase().getServices().getFileManager();
 
  252         List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER);
 
  254         if (files == null || files.isEmpty()) {
 
  260         for (AbstractFile file : files) {
 
  261             if (context.dataSourceIngestIsCancelled()) {
 
  264             getCookies(context, file);
 
  276     private void getHistory(IngestJobContext context, AbstractFile historyFile) 
throws TskCoreException, IOException {
 
  277         if (historyFile.getSize() == 0) {
 
  281         File tempHistoryFile = createTemporaryFile(context, historyFile);
 
  284             ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled);
 
  285         } 
catch (IOException ex) {
 
  286             throw new IOException(
"Error writingToFile: " + historyFile, ex); 
 
  290             Collection<BlackboardArtifact> bbartifacts = getHistoryArtifacts(historyFile, tempHistoryFile.toPath());
 
  291             if (!bbartifacts.isEmpty()) {
 
  292                 services.fireModuleDataEvent(
new ModuleDataEvent(
 
  293                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  294                         BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts));
 
  297             tempHistoryFile.delete();
 
  314     private void getBookmarks(IngestJobContext context, AbstractFile file) 
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
 
  315         if (file.getSize() == 0) {
 
  319         File tempFile = createTemporaryFile(context, file);
 
  322             Collection<BlackboardArtifact> bbartifacts = getBookmarkArtifacts(file, tempFile);
 
  323             if (!bbartifacts.isEmpty()) {
 
  324                 services.fireModuleDataEvent(
new ModuleDataEvent(
 
  325                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  326                         BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts));
 
  347     private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file) 
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
 
  348         if (file.getSize() == 0) {
 
  352         File tempFile = createTemporaryFile(context, file);
 
  355             Collection<BlackboardArtifact> bbartifacts = getDownloadArtifacts(dataSource, file, tempFile);
 
  356             if (!bbartifacts.isEmpty()) {
 
  357                 services.fireModuleDataEvent(
new ModuleDataEvent(
 
  358                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  359                         BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
 
  362             if (tempFile != null) {
 
  378     private void getCookies(IngestJobContext context, AbstractFile file) 
throws TskCoreException, IOException {
 
  379         if (file.getSize() == 0) {
 
  383         File tempFile = null;
 
  386             tempFile = createTemporaryFile(context, file);
 
  388             Collection<BlackboardArtifact> bbartifacts = getCookieArtifacts(file, tempFile);
 
  390             if (!bbartifacts.isEmpty()) {
 
  391                 services.fireModuleDataEvent(
new ModuleDataEvent(
 
  392                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  393                         BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts));
 
  396             if (tempFile != null) {
 
  412     private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath) 
throws TskCoreException {
 
  413         List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY);
 
  415         if (historyList == null || historyList.isEmpty()) {
 
  419         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  420         for (HashMap<String, Object> row : historyList) {
 
  421             String url = row.get(HEAD_URL).toString();
 
  422             String title = row.get(HEAD_TITLE).toString();
 
  423             Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
 
  425             BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
 
  426             bbart.addAttributes(createHistoryAttribute(url, time, null, title,
 
  427                     this.getName(), NetworkUtils.extractDomain(url), null));
 
  428             bbartifacts.add(bbart);
 
  447     private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile) 
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
 
  448         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  451             NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
 
  453             parseBookmarkDictionary(bbartifacts, origFile, root);
 
  454         } 
catch (PropertyListFormatException ex) {
 
  455             PropertyListFormatException plfe = 
new PropertyListFormatException(origFile.getName() + 
": " + ex.getMessage());
 
  456             plfe.setStackTrace(ex.getStackTrace());
 
  458         } 
catch (ParseException ex) {
 
  459             ParseException pe = 
new ParseException(origFile.getName() + 
": " + ex.getMessage(), ex.getErrorOffset());
 
  460             pe.setStackTrace(ex.getStackTrace());
 
  462         } 
catch (ParserConfigurationException ex) {
 
  463             ParserConfigurationException pce = 
new ParserConfigurationException(origFile.getName() + 
": " + ex.getMessage());
 
  464             pce.setStackTrace(ex.getStackTrace());
 
  466         } 
catch (SAXException ex) {
 
  467             SAXException se = 
new SAXException(origFile.getName() + 
": " + ex.getMessage());
 
  468             se.setStackTrace(ex.getStackTrace());
 
  488     private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
 
  489          Collection<BlackboardArtifact> bbartifacts = null;
 
  493                 NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile);
 
  498                 NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY);
 
  503                 NSObject[] objectArray = nsArray.getArray();
 
  504                 bbartifacts = 
new ArrayList<>();
 
  506                 for(NSObject obj: objectArray){
 
  507                     if(obj instanceof NSDictionary){
 
  508                         bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj));
 
  514         } 
catch (PropertyListFormatException ex) {
 
  515             PropertyListFormatException plfe = 
new PropertyListFormatException(origFile.getName() + 
": " + ex.getMessage());
 
  516             plfe.setStackTrace(ex.getStackTrace());
 
  518         } 
catch (ParseException ex) {
 
  519             ParseException pe = 
new ParseException(origFile.getName() + 
": " + ex.getMessage(), ex.getErrorOffset());
 
  520             pe.setStackTrace(ex.getStackTrace());
 
  522         } 
catch (ParserConfigurationException ex) {
 
  523             ParserConfigurationException pce = 
new ParserConfigurationException(origFile.getName() + 
": " + ex.getMessage());
 
  524             pce.setStackTrace(ex.getStackTrace());
 
  526         } 
catch (SAXException ex) {
 
  527             SAXException se = 
new SAXException(origFile.getName() + 
": " + ex.getMessage());
 
  528             se.setStackTrace(ex.getStackTrace());
 
  545     private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile) 
throws TskCoreException, IOException {
 
  546         Collection<BlackboardArtifact> bbartifacts = null;
 
  547         BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile);
 
  549         if (reader != null) {
 
  550             bbartifacts = 
new ArrayList<>();
 
  552             Iterator<Cookie> iter = reader.iterator();
 
  553             while (iter.hasNext()) {
 
  554                 Cookie cookie = iter.next();
 
  556                 BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
 
  557                 bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
 
  558                 bbartifacts.add(bbart);
 
  574     private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root) 
throws TskCoreException {
 
  575         if (root.containsKey(PLIST_KEY_CHILDREN)) {
 
  576             NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
 
  578             if (children != null) {
 
  579                 for (NSObject obj : children.getArray()) {
 
  580                     parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj);
 
  583         } 
else if (root.containsKey(PLIST_KEY_URL)) {
 
  587             NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
 
  589                 url = nsstr.toString();
 
  592             NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
 
  594             nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
 
  597                 title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
 
  600             if (url != null || title != null) {
 
  601                 BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
 
  602                 bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url)));
 
  603                 bbartifacts.add(bbart);
 
  617     private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry) 
throws TskCoreException {
 
  618         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  624         FileManager fileManager = getCurrentCase().getServices().getFileManager();
 
  626         NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
 
  627         if (nsstring != null) {
 
  628             url = nsstring.toString();
 
  631         nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
 
  632         if (nsstring != null) {
 
  633             path = nsstring.toString();
 
  634             pathID = Util.findID(dataSource, path);
 
  637         NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
 
  639             time = date.getDate().getTime();
 
  642         BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
 
  643         bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
 
  644         bbartifacts.add(bbart);
 
  647         for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
 
  648             BlackboardArtifact downloadSourceArt =  downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
 
  649             downloadSourceArt.addAttributes(createDownloadSourceAttributes(url));
 
  650             bbartifacts.add(downloadSourceArt);