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.nio.file.Paths;
32 import java.text.ParseException;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.logging.Level;
39 import javax.xml.parsers.ParserConfigurationException;
40 import org.apache.commons.io.FilenameUtils;
41 import org.openide.util.NbBundle.Messages;
52 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK;
53 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE;
54 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
55 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
58 import org.xml.sax.SAXException;
64 final class ExtractSafari
extends Extract {
66 private final IngestServices services = IngestServices.getInstance();
67 private final IngestJobContext context;
70 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;";
72 private static final String HISTORY_FILE_NAME =
"History.db";
73 private static final String BOOKMARK_FILE_NAME =
"Bookmarks.plist";
74 private static final String DOWNLOAD_FILE_NAME =
"Downloads.plist";
75 private static final String COOKIE_FILE_NAME =
"Cookies.binarycookies";
76 private static final String COOKIE_FOLDER =
"Cookies";
77 private static final String SAFARI_FOLDER =
"Safari";
79 private static final String HEAD_URL =
"url";
80 private static final String HEAD_TITLE =
"title";
81 private static final String HEAD_TIME =
"time";
83 private static final String PLIST_KEY_CHILDREN =
"Children";
84 private static final String PLIST_KEY_URL =
"URLString";
85 private static final String PLIST_KEY_URI =
"URIDictionary";
86 private static final String PLIST_KEY_TITLE =
"title";
87 private static final String PLIST_KEY_DOWNLOAD_URL =
"DownloadEntryURL";
88 private static final String PLIST_KEY_DOWNLOAD_DATE =
"DownloadEntryDateAddedKey";
89 private static final String PLIST_KEY_DOWNLOAD_PATH =
"DownloadEntryPath";
90 private static final String PLIST_KEY_DOWNLOAD_HISTORY =
"DownloadHistory";
92 private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName());
95 "ExtractSafari_Module_Name=Safari Analyzer",
96 "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.",
97 "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",
98 "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files",
99 "Progress_Message_Safari_History=Safari History",
100 "Progress_Message_Safari_Bookmarks=Safari Bookmarks",
101 "Progress_Message_Safari_Cookies=Safari Cookies",
102 "Progress_Message_Safari_Downloads=Safari Downloads",})
104 ExtractSafari(IngestJobContext context) {
105 super(Bundle.ExtractSafari_Module_Name(), context);
106 this.context = context;
110 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
113 progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
115 processHistoryDB(dataSource);
117 }
catch (IOException | TskCoreException ex) {
118 this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History());
119 LOG.log(Level.SEVERE,
"Exception thrown while processing history file.", ex);
122 if (context.dataSourceIngestIsCancelled()) {
126 progressBar.progress(Bundle.Progress_Message_Safari_Bookmarks());
128 processBookmarkPList(dataSource);
129 }
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
130 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
131 LOG.log(Level.SEVERE,
"Exception thrown while parsing Safari Bookmarks file.", ex);
134 if (context.dataSourceIngestIsCancelled()) {
138 progressBar.progress(Bundle.Progress_Message_Safari_Downloads());
140 processDownloadsPList(dataSource);
141 }
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
142 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
143 LOG.log(Level.SEVERE,
"Exception thrown while parsing Safari Download.plist file.", ex);
146 if (context.dataSourceIngestIsCancelled()) {
150 progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
152 processBinaryCookieFile(dataSource);
153 }
catch (TskCoreException ex) {
154 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
155 LOG.log(Level.SEVERE,
"Exception thrown while processing Safari cookies file.", ex);
166 private void processHistoryDB(Content dataSource)
throws TskCoreException, IOException {
167 FileManager fileManager = getCurrentCase().getServices().getFileManager();
169 List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER);
171 if (historyFiles == null || historyFiles.isEmpty()) {
177 for (AbstractFile historyFile : historyFiles) {
178 if (context.dataSourceIngestIsCancelled()) {
182 getHistory(historyFile);
199 private void processBookmarkPList(Content dataSource)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
200 FileManager fileManager = getCurrentCase().getServices().getFileManager();
202 List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER);
204 if (files == null || files.isEmpty()) {
210 for (AbstractFile file : files) {
211 if (context.dataSourceIngestIsCancelled()) {
232 private void processDownloadsPList(Content dataSource)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
233 FileManager fileManager = getCurrentCase().getServices().getFileManager();
235 List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER);
237 if (files == null || files.isEmpty()) {
243 for (AbstractFile file : files) {
244 if (context.dataSourceIngestIsCancelled()) {
248 getDownloads(dataSource, file);
261 private void processBinaryCookieFile(Content dataSource)
throws TskCoreException {
262 FileManager fileManager = getCurrentCase().getServices().getFileManager();
264 List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER);
266 if (files == null || files.isEmpty()) {
272 for (AbstractFile file : files) {
273 if (context.dataSourceIngestIsCancelled()) {
278 }
catch (IOException ex) {
279 LOG.log(Level.WARNING, String.format(
"Failed to get cookies from file %s", Paths.get(file.getUniquePath(), file.getName()).toString()), ex);
293 private void getHistory(AbstractFile historyFile)
throws TskCoreException, IOException {
294 if (historyFile.getSize() == 0) {
297 File tempHistoryFile = createTemporaryFile(historyFile);
299 postArtifacts(getHistoryArtifacts(historyFile, tempHistoryFile.toPath()));
301 tempHistoryFile.delete();
318 private void getBookmarks(AbstractFile file)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
319 if (file.getSize() == 0) {
322 File tempFile = createTemporaryFile(file);
324 postArtifacts(getBookmarkArtifacts(file, tempFile));
343 private void getDownloads(Content dataSource, AbstractFile file)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
344 if (file.getSize() == 0) {
347 File tempFile = createTemporaryFile(file);
349 postArtifacts(getDownloadArtifacts(dataSource, file, tempFile));
364 private void getCookies(AbstractFile file)
throws TskCoreException, IOException {
365 if (file.getSize() == 0) {
369 File tempFile = null;
372 tempFile = createTemporaryFile(file);
374 if (!context.dataSourceIngestIsCancelled()) {
375 postArtifacts(getCookieArtifacts(file, tempFile));
379 if (tempFile != null) {
397 private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath)
throws TskCoreException {
398 List<HashMap<String, Object>> historyList = this.querySQLiteDb(tempFilePath.toString(), HISTORY_QUERY);
400 if (historyList == null || historyList.isEmpty()) {
404 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
405 for (HashMap<String, Object> row : historyList) {
406 if (context.dataSourceIngestIsCancelled()) {
410 String url = row.get(HEAD_URL).toString();
411 String title = row.get(HEAD_TITLE).toString();
412 Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
415 createArtifactWithAttributes(
416 BlackboardArtifact.Type.TSK_WEB_HISTORY,
418 createHistoryAttributes(url, time, null, title,
419 this.getDisplayName(), NetworkUtils.extractDomain(url), null)));
440 private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile)
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
441 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
444 NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
446 parseBookmarkDictionary(bbartifacts, origFile, root);
447 }
catch (PropertyListFormatException ex) {
448 PropertyListFormatException plfe =
new PropertyListFormatException(origFile.getName() +
": " + ex.getMessage());
449 plfe.setStackTrace(ex.getStackTrace());
451 }
catch (ParseException ex) {
452 ParseException pe =
new ParseException(origFile.getName() +
": " + ex.getMessage(), ex.getErrorOffset());
453 pe.setStackTrace(ex.getStackTrace());
455 }
catch (ParserConfigurationException ex) {
456 ParserConfigurationException pce =
new ParserConfigurationException(origFile.getName() +
": " + ex.getMessage());
457 pce.setStackTrace(ex.getStackTrace());
459 }
catch (SAXException ex) {
460 SAXException se =
new SAXException(origFile.getName() +
": " + ex.getMessage());
461 se.setStackTrace(ex.getStackTrace());
484 private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
485 Collection<BlackboardArtifact> bbartifacts = null;
489 NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
495 NSArray nsArray = (NSArray) root.get(PLIST_KEY_DOWNLOAD_HISTORY);
497 if (nsArray == null) {
501 NSObject[] objectArray = nsArray.getArray();
502 bbartifacts =
new ArrayList<>();
504 for (NSObject obj : objectArray) {
505 if (obj instanceof NSDictionary) {
506 bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary) obj));
512 }
catch (PropertyListFormatException ex) {
513 PropertyListFormatException plfe =
new PropertyListFormatException(origFile.getName() +
": " + ex.getMessage());
514 plfe.setStackTrace(ex.getStackTrace());
516 }
catch (ParseException ex) {
517 ParseException pe =
new ParseException(origFile.getName() +
": " + ex.getMessage(), ex.getErrorOffset());
518 pe.setStackTrace(ex.getStackTrace());
520 }
catch (ParserConfigurationException ex) {
521 ParserConfigurationException pce =
new ParserConfigurationException(origFile.getName() +
": " + ex.getMessage());
522 pce.setStackTrace(ex.getStackTrace());
524 }
catch (SAXException ex) {
525 SAXException se =
new SAXException(origFile.getName() +
": " + ex.getMessage());
526 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 if (context.dataSourceIngestIsCancelled()) {
558 Cookie cookie = iter.next();
561 createArtifactWithAttributes(
562 BlackboardArtifact.Type.TSK_WEB_COOKIE,
564 createCookieAttributes(
566 cookie.getCreationDate(),
568 cookie.getExpirationDate(),
569 cookie.getName(), cookie.getValue(),
570 this.getDisplayName(),
571 NetworkUtils.extractDomain(cookie.getURL()))));
588 private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root)
throws TskCoreException {
590 if (context.dataSourceIngestIsCancelled()) {
594 if (root.containsKey(PLIST_KEY_CHILDREN)) {
595 NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
597 if (children != null) {
598 for (NSObject obj : children.getArray()) {
599 parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj);
602 }
else if (root.containsKey(PLIST_KEY_URL)) {
606 NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
608 url = nsstr.toString();
611 NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
613 nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
616 title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
619 if (url != null || title != null) {
620 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, origFile,
621 createBookmarkAttributes(url,
625 NetworkUtils.extractDomain(url))));
641 private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry)
throws TskCoreException {
642 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
648 NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
649 if (nsstring != null) {
650 url = nsstring.toString();
653 nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
654 if (nsstring != null) {
655 path = nsstring.toString();
656 pathID = Util.findID(dataSource, path);
659 NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
661 time = date.getDate().getTime();
664 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_DOWNLOAD, origFile, createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getDisplayName()));
665 bbartifacts.add(webDownloadArtifact);
668 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
669 FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
670 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));