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;
56 import org.xml.sax.SAXException;
62 final class ExtractSafari
extends Extract {
64 private final IngestServices services = IngestServices.getInstance();
67 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;";
69 private static final String HISTORY_FILE_NAME =
"History.db";
70 private static final String BOOKMARK_FILE_NAME =
"Bookmarks.plist";
71 private static final String DOWNLOAD_FILE_NAME =
"Downloads.plist";
72 private static final String COOKIE_FILE_NAME =
"Cookies.binarycookies";
73 private static final String COOKIE_FOLDER =
"Cookies";
74 private static final String SAFARI_FOLDER =
"Safari";
76 private static final String HEAD_URL =
"url";
77 private static final String HEAD_TITLE =
"title";
78 private static final String HEAD_TIME =
"time";
80 private static final String PLIST_KEY_CHILDREN =
"Children";
81 private static final String PLIST_KEY_URL =
"URLString";
82 private static final String PLIST_KEY_URI =
"URIDictionary";
83 private static final String PLIST_KEY_TITLE =
"title";
84 private static final String PLIST_KEY_DOWNLOAD_URL =
"DownloadEntryURL";
85 private static final String PLIST_KEY_DOWNLOAD_DATE =
"DownloadEntryDateAddedKey";
86 private static final String PLIST_KEY_DOWNLOAD_PATH =
"DownloadEntryPath";
87 private static final String PLIST_KEY_DOWNLOAD_HISTORY =
"DownloadHistory";
89 private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName());
92 "ExtractSafari_Module_Name=Safari",
93 "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.",
94 "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",
95 "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files",
96 "Progress_Message_Safari_History=Safari History",
97 "Progress_Message_Safari_Bookmarks=Safari Bookmarks",
98 "Progress_Message_Safari_Cookies=Safari Cookies",
99 "Progress_Message_Safari_Downloads=Safari Downloads",
111 protected String getName() {
112 return Bundle.ExtractSafari_Module_Name();
116 void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
119 progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
121 processHistoryDB(dataSource, context);
123 }
catch (IOException | TskCoreException ex) {
124 this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History());
125 LOG.log(Level.SEVERE,
"Exception thrown while processing history file.", ex);
128 progressBar.progress(Bundle.Progress_Message_Safari_Bookmarks());
130 processBookmarkPList(dataSource, context);
131 }
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
132 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
133 LOG.log(Level.SEVERE,
"Exception thrown while parsing Safari Bookmarks file.", ex);
136 progressBar.progress(Bundle.Progress_Message_Safari_Downloads());
138 processDownloadsPList(dataSource, context);
139 }
catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
140 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
141 LOG.log(Level.SEVERE,
"Exception thrown while parsing Safari Download.plist file.", ex);
144 progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
146 processBinaryCookieFile(dataSource, context);
147 }
catch (TskCoreException ex) {
148 this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
149 LOG.log(Level.SEVERE,
"Exception thrown while processing Safari cookies file.", ex);
160 private void processHistoryDB(Content dataSource, IngestJobContext context)
throws TskCoreException, IOException {
161 FileManager fileManager = getCurrentCase().getServices().getFileManager();
163 List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER);
165 if (historyFiles == null || historyFiles.isEmpty()) {
171 for (AbstractFile historyFile : historyFiles) {
172 if (context.dataSourceIngestIsCancelled()) {
176 getHistory(context, historyFile);
191 private void processBookmarkPList(Content dataSource, IngestJobContext context)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
192 FileManager fileManager = getCurrentCase().getServices().getFileManager();
194 List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER);
196 if (files == null || files.isEmpty()) {
202 for (AbstractFile file : files) {
203 if (context.dataSourceIngestIsCancelled()) {
207 getBookmarks(context, file);
223 private void processDownloadsPList(Content dataSource, IngestJobContext context)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
224 FileManager fileManager = getCurrentCase().getServices().getFileManager();
226 List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER);
228 if (files == null || files.isEmpty()) {
234 for (AbstractFile file : files) {
235 if (context.dataSourceIngestIsCancelled()) {
239 getDownloads(dataSource, context, file);
250 private void processBinaryCookieFile(Content dataSource, IngestJobContext context)
throws TskCoreException {
251 FileManager fileManager = getCurrentCase().getServices().getFileManager();
253 List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER);
255 if (files == null || files.isEmpty()) {
261 for (AbstractFile file : files) {
262 if (context.dataSourceIngestIsCancelled()) {
266 getCookies(context, file);
267 }
catch (IOException ex) {
268 LOG.log(Level.WARNING, String.format(
"Failed to get cookies from file %s", Paths.get(file.getUniquePath(), file.getName()).toString()), ex);
281 private void getHistory(IngestJobContext context, AbstractFile historyFile)
throws TskCoreException, IOException {
282 if (historyFile.getSize() == 0) {
286 File tempHistoryFile = createTemporaryFile(context, historyFile);
289 ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled);
290 }
catch (IOException ex) {
291 throw new IOException(
"Error writingToFile: " + historyFile, ex);
295 postArtifacts(getHistoryArtifacts(historyFile, tempHistoryFile.toPath(), context));
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 postArtifacts(getBookmarkArtifacts(file, tempFile, context));
342 private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file)
throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
343 if (file.getSize() == 0) {
347 File tempFile = createTemporaryFile(context, file);
350 postArtifacts(getDownloadArtifacts(dataSource, file, tempFile));
353 if (tempFile != null) {
369 private void getCookies(IngestJobContext context, AbstractFile file)
throws TskCoreException, IOException {
370 if (file.getSize() == 0) {
374 File tempFile = null;
377 tempFile = createTemporaryFile(context, file);
379 postArtifacts(getCookieArtifacts(file, tempFile, context));
382 if (tempFile != null) {
398 private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath, IngestJobContext context)
throws TskCoreException {
399 List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY);
401 if (historyList == null || historyList.isEmpty()) {
405 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
406 for (HashMap<String, Object> row : historyList) {
407 if (context.dataSourceIngestIsCancelled()) {
411 String url = row.get(HEAD_URL).toString();
412 String title = row.get(HEAD_TITLE).toString();
413 Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
415 BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
416 bbart.addAttributes(createHistoryAttribute(url, time, null, title,
417 this.getName(), NetworkUtils.extractDomain(url), null));
418 bbartifacts.add(bbart);
437 private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context)
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
438 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
441 NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
443 parseBookmarkDictionary(bbartifacts, origFile, root, context);
444 }
catch (PropertyListFormatException ex) {
445 PropertyListFormatException plfe =
new PropertyListFormatException(origFile.getName() +
": " + ex.getMessage());
446 plfe.setStackTrace(ex.getStackTrace());
448 }
catch (ParseException ex) {
449 ParseException pe =
new ParseException(origFile.getName() +
": " + ex.getMessage(), ex.getErrorOffset());
450 pe.setStackTrace(ex.getStackTrace());
452 }
catch (ParserConfigurationException ex) {
453 ParserConfigurationException pce =
new ParserConfigurationException(origFile.getName() +
": " + ex.getMessage());
454 pce.setStackTrace(ex.getStackTrace());
456 }
catch (SAXException ex) {
457 SAXException se =
new SAXException(origFile.getName() +
": " + ex.getMessage());
458 se.setStackTrace(ex.getStackTrace());
478 private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)
throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
479 Collection<BlackboardArtifact> bbartifacts = null;
483 NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile);
488 NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY);
493 NSObject[] objectArray = nsArray.getArray();
494 bbartifacts =
new ArrayList<>();
496 for(NSObject obj: objectArray){
497 if(obj instanceof NSDictionary){
498 bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj));
504 }
catch (PropertyListFormatException ex) {
505 PropertyListFormatException plfe =
new PropertyListFormatException(origFile.getName() +
": " + ex.getMessage());
506 plfe.setStackTrace(ex.getStackTrace());
508 }
catch (ParseException ex) {
509 ParseException pe =
new ParseException(origFile.getName() +
": " + ex.getMessage(), ex.getErrorOffset());
510 pe.setStackTrace(ex.getStackTrace());
512 }
catch (ParserConfigurationException ex) {
513 ParserConfigurationException pce =
new ParserConfigurationException(origFile.getName() +
": " + ex.getMessage());
514 pce.setStackTrace(ex.getStackTrace());
516 }
catch (SAXException ex) {
517 SAXException se =
new SAXException(origFile.getName() +
": " + ex.getMessage());
518 se.setStackTrace(ex.getStackTrace());
535 private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context)
throws TskCoreException, IOException {
536 Collection<BlackboardArtifact> bbartifacts = null;
537 BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile);
539 if (reader != null) {
540 bbartifacts =
new ArrayList<>();
542 Iterator<Cookie> iter = reader.iterator();
543 while (iter.hasNext()) {
544 if (context.dataSourceIngestIsCancelled()) {
548 Cookie cookie = iter.next();
550 BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
551 bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
552 bbartifacts.add(bbart);
568 private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root, IngestJobContext context)
throws TskCoreException {
570 if (context.dataSourceIngestIsCancelled()) {
574 if (root.containsKey(PLIST_KEY_CHILDREN)) {
575 NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
577 if (children != null) {
578 for (NSObject obj : children.getArray()) {
579 parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj, context);
582 }
else if (root.containsKey(PLIST_KEY_URL)) {
586 NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
588 url = nsstr.toString();
591 NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
593 nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
596 title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
599 if (url != null || title != null) {
600 BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
601 bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url)));
602 bbartifacts.add(bbart);
616 private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry)
throws TskCoreException {
617 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
623 FileManager fileManager = getCurrentCase().getServices().getFileManager();
625 NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
626 if (nsstring != null) {
627 url = nsstring.toString();
630 nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
631 if (nsstring != null) {
632 path = nsstring.toString();
633 pathID = Util.findID(dataSource, path);
636 NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
638 time = date.getDate().getTime();
641 BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
642 webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
643 bbartifacts.add(webDownloadArtifact);
646 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
647 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
648 associatedObjectArtifact.addAttribute(
649 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
650 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
651 bbartifacts.add(associatedObjectArtifact);