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);