23 package org.sleuthkit.autopsy.recentactivity;
25 import com.google.gson.JsonArray;
26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonIOException;
28 import com.google.gson.JsonObject;
29 import com.google.gson.JsonParser;
30 import com.google.gson.JsonSyntaxException;
32 import java.io.FileNotFoundException;
33 import java.io.FileReader;
34 import java.io.IOException;
35 import java.io.UnsupportedEncodingException;
36 import java.net.URLDecoder;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
44 import java.util.logging.Level;
45 import org.apache.commons.io.FilenameUtils;
46 import org.openide.util.NbBundle;
47 import org.openide.util.NbBundle.Messages;
60 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
62 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
64 import org.
sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
67 "Progress_Message_Firefox_History=Firefox History",
68 "Progress_Message_Firefox_Bookmarks=Firefox Bookmarks",
69 "Progress_Message_Firefox_Cookies=Firefox Cookies",
70 "Progress_Message_Firefox_Downloads=Firefox Downloads",
71 "Progress_Message_Firefox_FormHistory=Firefox Form History",
72 "Progress_Message_Firefox_AutoFill=Firefox Auto Fill"
78 class Firefox extends Extract {
80 private static final Logger logger = Logger.getLogger(Firefox.class.getName());
81 private static final String PLACE_URL_PREFIX =
"place:";
82 private static final String HISTORY_QUERY =
"SELECT moz_historyvisits.id, url, title, visit_count,(visit_date/1000000) AS visit_date,from_visit,"
83 +
"(SELECT url FROM moz_historyvisits history, moz_places places where history.id = moz_historyvisits.from_visit and history.place_id = places.id ) as ref "
84 +
"FROM moz_places, moz_historyvisits "
85 +
"WHERE moz_places.id = moz_historyvisits.place_id "
87 private static final String COOKIE_QUERY =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies";
88 private static final String COOKIE_QUERY_V3 =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies";
89 private static final String BOOKMARK_QUERY =
"SELECT fk, moz_bookmarks.title, url, (moz_bookmarks.dateAdded/1000000) AS dateAdded FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id";
90 private static final String DOWNLOAD_QUERY =
"SELECT target, source,(startTime/1000000) AS startTime, maxBytes FROM moz_downloads";
91 private static final String DOWNLOAD_QUERY_V24 =
"SELECT url, content AS target, (lastModified/1000000) AS lastModified "
92 +
" FROM moz_places, moz_annos, moz_anno_attributes "
93 +
" WHERE moz_places.id = moz_annos.place_id"
94 +
" AND moz_annos.anno_attribute_id = moz_anno_attributes.id"
95 +
" AND moz_anno_attributes.name='downloads/destinationFileURI'";
96 private static final String FORMHISTORY_QUERY =
"SELECT fieldname, value FROM moz_formhistory";
97 private static final String FORMHISTORY_QUERY_V64 =
"SELECT fieldname, value, timesUsed, firstUsed, lastUsed FROM moz_formhistory";
98 private Content dataSource;
99 private final IngestJobContext context;
101 Firefox(IngestJobContext context) {
102 super(NbBundle.getMessage(Firefox.class,
"Firefox.moduleName"), context);
103 this.context = context;
107 public void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
108 this.dataSource = dataSource;
110 long ingestJobId = context.getJobId();
112 progressBar.progress(Bundle.Progress_Message_Firefox_History());
113 this.getHistory(context.getJobId());
115 if (context.dataSourceIngestIsCancelled()) {
119 progressBar.progress(Bundle.Progress_Message_Firefox_Bookmarks());
120 this.getBookmark(ingestJobId);
122 if (context.dataSourceIngestIsCancelled()) {
126 progressBar.progress(Bundle.Progress_Message_Firefox_Downloads());
127 this.getDownload(ingestJobId);
129 if (context.dataSourceIngestIsCancelled()) {
133 progressBar.progress(Bundle.Progress_Message_Firefox_Cookies());
134 this.getCookie(ingestJobId);
136 if (context.dataSourceIngestIsCancelled()) {
140 progressBar.progress(Bundle.Progress_Message_Firefox_FormHistory());
141 this.getFormsHistory(ingestJobId);
143 if (context.dataSourceIngestIsCancelled()) {
147 progressBar.progress(Bundle.Progress_Message_Firefox_AutoFill());
148 this.getAutofillProfiles(ingestJobId);
156 private void getHistory(
long ingestJobId) {
157 FileManager fileManager = currentCase.getServices().getFileManager();
158 List<AbstractFile> historyFiles;
160 historyFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
161 }
catch (TskCoreException ex) {
162 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.errFetchingFiles");
163 logger.log(Level.WARNING, msg);
164 this.addErrorMessage(this.getDisplayName() +
": " + msg);
168 if (historyFiles.isEmpty()) {
169 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.noFilesFound");
170 logger.log(Level.INFO, msg);
175 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
177 for (AbstractFile historyFile : historyFiles) {
179 if (context.dataSourceIngestIsCancelled()) {
183 if (historyFile.getSize() == 0) {
187 String fileName = historyFile.getName();
188 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
190 ContentUtils.writeToFile(historyFile,
new File(temps), context::dataSourceIngestIsCancelled);
191 }
catch (ReadContentInputStreamException ex) {
192 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
193 fileName, historyFile.getId()), ex);
194 this.addErrorMessage(
195 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
198 }
catch (IOException ex) {
199 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
200 temps, fileName, historyFile.getId()), ex);
201 this.addErrorMessage(
202 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
206 File dbFile =
new File(temps);
207 if (context.dataSourceIngestIsCancelled()) {
211 List<HashMap<String, Object>> tempList = this.querySQLiteDb(temps, HISTORY_QUERY);
212 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), temps, tempList.size()});
213 for (HashMap<String, Object> result : tempList) {
215 if (context.dataSourceIngestIsCancelled()) {
219 String url = result.get(
"url").toString();
221 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
222 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
223 RecentActivityExtracterModuleFactory.getModuleName(),
224 ((url != null) ? url :
"")));
226 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
227 RecentActivityExtracterModuleFactory.getModuleName(),
228 (Long.valueOf(result.get(
"visit_date").toString()))));
229 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
230 RecentActivityExtracterModuleFactory.getModuleName(),
231 ((result.get(
"ref").toString() != null) ? result.get(
"ref").toString() :
"")));
232 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
233 RecentActivityExtracterModuleFactory.getModuleName(),
234 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
235 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
236 RecentActivityExtracterModuleFactory.getModuleName(),
237 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
238 String domain = extractDomain(url);
239 if (domain != null && domain.isEmpty() ==
false) {
240 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
241 RecentActivityExtracterModuleFactory.getModuleName(), domain));
246 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_HISTORY, historyFile, bbattributes));
247 }
catch (TskCoreException ex) {
248 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_HISTORY artifact for file %d", historyFile.getId()), ex);
255 if (!context.dataSourceIngestIsCancelled()) {
256 postArtifacts(bbartifacts);
265 private void getBookmark(
long ingestJobId) {
267 FileManager fileManager = currentCase.getServices().getFileManager();
268 List<AbstractFile> bookmarkFiles;
270 bookmarkFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
271 }
catch (TskCoreException ex) {
272 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getBookmark.errMsg.errFetchFiles");
273 logger.log(Level.WARNING, msg);
274 this.addErrorMessage(this.getDisplayName() +
": " + msg);
278 if (bookmarkFiles.isEmpty()) {
279 logger.log(Level.INFO,
"Didn't find any firefox bookmark files.");
284 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
286 for (AbstractFile bookmarkFile : bookmarkFiles) {
287 if (bookmarkFile.getSize() == 0) {
290 String fileName = bookmarkFile.getName();
291 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
293 ContentUtils.writeToFile(bookmarkFile,
new File(temps), context::dataSourceIngestIsCancelled);
294 }
catch (ReadContentInputStreamException ex) {
295 logger.log(Level.WARNING, String.format(
"Error reading Firefox bookmark artifacts file '%s' (id=%d).",
296 fileName, bookmarkFile.getId()), ex);
297 this.addErrorMessage(
298 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
301 }
catch (IOException ex) {
302 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox bookmark artifacts file '%s' (id=%d).",
303 temps, fileName, bookmarkFile.getId()), ex);
304 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getBookmark.errMsg.errAnalyzeFile",
305 this.getDisplayName(), fileName));
308 File dbFile =
new File(temps);
309 if (context.dataSourceIngestIsCancelled()) {
313 List<HashMap<String, Object>> tempList = this.querySQLiteDb(temps, BOOKMARK_QUERY);
314 logger.log(Level.INFO,
"{0} - Now getting bookmarks from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), temps, tempList.size()});
315 for (HashMap<String, Object> result : tempList) {
317 if (context.dataSourceIngestIsCancelled()) {
321 String url = result.get(
"url").toString();
323 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
324 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
325 RecentActivityExtracterModuleFactory.getModuleName(),
326 ((url != null) ? url :
"")));
327 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
328 RecentActivityExtracterModuleFactory.getModuleName(),
329 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
330 if (Long.valueOf(result.get(
"dateAdded").toString()) > 0) {
331 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
332 RecentActivityExtracterModuleFactory.getModuleName(),
333 (Long.valueOf(result.get(
"dateAdded").toString()))));
335 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
336 RecentActivityExtracterModuleFactory.getModuleName(),
337 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
338 String domain = extractDomain(url);
339 if (domain != null && domain.isEmpty() ==
false) {
340 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
341 RecentActivityExtracterModuleFactory.getModuleName(), domain));
345 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes));
346 }
catch (TskCoreException ex) {
347 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_BOOKMARK artifact for file %d", bookmarkFile.getId()), ex);
354 if (!context.dataSourceIngestIsCancelled()) {
355 postArtifacts(bbartifacts);
364 private void getCookie(
long ingestJobId) {
365 FileManager fileManager = currentCase.getServices().getFileManager();
366 List<AbstractFile> cookiesFiles;
368 cookiesFiles = fileManager.findFiles(dataSource,
"cookies.sqlite",
"Firefox");
369 }
catch (TskCoreException ex) {
370 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getCookie.errMsg.errFetchFile");
371 logger.log(Level.WARNING, msg);
372 this.addErrorMessage(this.getDisplayName() +
": " + msg);
376 if (cookiesFiles.isEmpty()) {
377 logger.log(Level.INFO,
"Didn't find any Firefox cookie files.");
382 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
384 for (AbstractFile cookiesFile : cookiesFiles) {
385 if (context.dataSourceIngestIsCancelled()) {
389 if (cookiesFile.getSize() == 0) {
392 String fileName = cookiesFile.getName();
393 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
395 ContentUtils.writeToFile(cookiesFile,
new File(temps), context::dataSourceIngestIsCancelled);
396 }
catch (ReadContentInputStreamException ex) {
397 logger.log(Level.WARNING, String.format(
"Error reading Firefox cookie artifacts file '%s' (id=%d).",
398 fileName, cookiesFile.getId()), ex);
399 this.addErrorMessage(
400 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
403 }
catch (IOException ex) {
404 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox cookie artifacts file '%s' (id=%d).",
405 temps, fileName, cookiesFile.getId()), ex);
406 this.addErrorMessage(
407 NbBundle.getMessage(
this.getClass(),
"Firefox.getCookie.errMsg.errAnalyzeFile", this.getDisplayName(),
411 File dbFile =
new File(temps);
412 if (context.dataSourceIngestIsCancelled()) {
416 boolean checkColumn = Util.checkColumn(
"creationTime",
"moz_cookies", temps);
419 query = COOKIE_QUERY;
421 query = COOKIE_QUERY_V3;
424 List<HashMap<String, Object>> tempList = this.querySQLiteDb(temps, query);
425 logger.log(Level.INFO,
"{0} - Now getting cookies from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), temps, tempList.size()});
426 for (HashMap<String, Object> result : tempList) {
428 if (context.dataSourceIngestIsCancelled()) {
432 String host = result.get(
"host").toString();
434 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
435 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
436 RecentActivityExtracterModuleFactory.getModuleName(),
437 ((host != null) ? host :
"")));
438 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
439 RecentActivityExtracterModuleFactory.getModuleName(),
440 (Long.valueOf(result.get(
"lastAccessed").toString()))));
441 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
442 RecentActivityExtracterModuleFactory.getModuleName(),
443 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
444 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
445 RecentActivityExtracterModuleFactory.getModuleName(),
446 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
447 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
448 RecentActivityExtracterModuleFactory.getModuleName(),
449 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
451 if (checkColumn ==
true) {
452 String value = result.get(
"creationTime").toString();
453 if(value != null && !value.isEmpty()) {
454 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
455 RecentActivityExtracterModuleFactory.getModuleName(),
456 (Long.valueOf(result.get(
"creationTime").toString()))));
459 String domain = extractDomain(host);
460 if (domain != null && domain.isEmpty() ==
false) {
461 domain = domain.replaceFirst(
"^\\.+(?!$)",
"");
462 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
463 RecentActivityExtracterModuleFactory.getModuleName(), domain));
467 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_COOKIE, cookiesFile, bbattributes));
468 }
catch (TskCoreException ex) {
469 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_COOKIE artifact for file %d", cookiesFile.getId()), ex);
476 if (!context.dataSourceIngestIsCancelled()) {
477 postArtifacts(bbartifacts);
486 private void getDownload(
long ingestJobId) {
487 getDownloadPreVersion24(ingestJobId);
488 getDownloadVersion24(ingestJobId);
498 private void getDownloadPreVersion24(
long ingestJobId) {
500 FileManager fileManager = currentCase.getServices().getFileManager();
501 List<AbstractFile> downloadsFiles;
503 downloadsFiles = fileManager.findFiles(dataSource,
"downloads.sqlite",
"Firefox");
504 }
catch (TskCoreException ex) {
505 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlPre24.errMsg.errFetchFiles");
506 logger.log(Level.WARNING, msg);
507 this.addErrorMessage(this.getDisplayName() +
": " + msg);
511 if (downloadsFiles.isEmpty()) {
512 logger.log(Level.INFO,
"Didn't find any pre-version-24.0 Firefox download files.");
517 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
519 for (AbstractFile downloadsFile : downloadsFiles) {
520 if (downloadsFile.getSize() == 0) {
523 String fileName = downloadsFile.getName();
524 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
527 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
528 }
catch (ReadContentInputStreamException ex) {
529 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
530 fileName, downloadsFile.getId()), ex);
531 this.addErrorMessage(
532 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
535 }
catch (IOException ex) {
536 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
537 temps, fileName, downloadsFile.getId()), ex);
538 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errAnalyzeFiles",
539 this.getDisplayName(), fileName));
542 File dbFile =
new File(temps);
543 if (context.dataSourceIngestIsCancelled()) {
548 List<HashMap<String, Object>> tempList = this.querySQLiteDb(temps, DOWNLOAD_QUERY);
549 logger.log(Level.INFO,
"{0}- Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), temps, tempList.size()});
550 for (HashMap<String, Object> result : tempList) {
552 if (context.dataSourceIngestIsCancelled()) {
556 String source = result.get(
"source").toString();
558 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
560 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
561 RecentActivityExtracterModuleFactory.getModuleName(),
564 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
565 RecentActivityExtracterModuleFactory.getModuleName(),
566 (Long.valueOf(result.get(
"startTime").toString()))));
568 String target = result.get(
"target").toString();
569 String downloadedFilePath =
"";
570 if (target != null) {
572 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
573 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
574 RecentActivityExtracterModuleFactory.getModuleName(),
575 downloadedFilePath));
576 long pathID = Util.findID(dataSource, downloadedFilePath);
578 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
579 RecentActivityExtracterModuleFactory.getModuleName(),
582 }
catch (UnsupportedEncodingException ex) {
583 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
588 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
589 RecentActivityExtracterModuleFactory.getModuleName(),
590 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
591 String domain = extractDomain(source);
592 if (domain != null && domain.isEmpty() ==
false) {
593 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
594 RecentActivityExtracterModuleFactory.getModuleName(),
598 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
599 bbartifacts.add(webDownloadArtifact);
602 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
603 FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
604 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
607 }
catch (TskCoreException ex) {
608 logger.log(Level.SEVERE, String.format(
"Error creating TSK_WEB_DOWNLOAD or TSK_ASSOCIATED_ARTIFACT artifact for file '%d'",
609 downloadsFile.getId()), ex);
614 this.addErrorMessage(
615 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errParsingArtifacts",
616 this.getDisplayName(), errors));
622 if (!context.dataSourceIngestIsCancelled()) {
623 postArtifacts(bbartifacts);
634 private void getDownloadVersion24(
long ingestJobId) {
635 FileManager fileManager = currentCase.getServices().getFileManager();
636 List<AbstractFile> downloadsFiles;
638 downloadsFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
639 }
catch (TskCoreException ex) {
640 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlV24.errMsg.errFetchFiles");
641 logger.log(Level.WARNING, msg);
642 this.addErrorMessage(this.getDisplayName() +
": " + msg);
646 if (downloadsFiles.isEmpty()) {
647 logger.log(Level.INFO,
"Didn't find any version-24.0 Firefox download files.");
652 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
654 for (AbstractFile downloadsFile : downloadsFiles) {
655 if (downloadsFile.getSize() == 0) {
658 String fileName = downloadsFile.getName();
659 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName +
"-downloads" + j +
".db";
662 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
663 }
catch (ReadContentInputStreamException ex) {
664 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
665 fileName, downloadsFile.getId()), ex);
666 this.addErrorMessage(
667 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getDisplayName(),
670 }
catch (IOException ex) {
671 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
672 temps, fileName, downloadsFile.getId()), ex);
673 this.addErrorMessage(
674 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errAnalyzeFile", this.getDisplayName(),
678 File dbFile =
new File(temps);
679 if (context.dataSourceIngestIsCancelled()) {
684 List<HashMap<String, Object>> tempList = this.querySQLiteDb(temps, DOWNLOAD_QUERY_V24);
686 logger.log(Level.INFO,
"{0} - Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), temps, tempList.size()});
687 for (HashMap<String, Object> result : tempList) {
689 if (context.dataSourceIngestIsCancelled()) {
693 String url = result.get(
"url").toString();
695 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
697 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
698 RecentActivityExtracterModuleFactory.getModuleName(),
704 String target = result.get(
"target").toString();
705 String downloadedFilePath =
"";
706 if (target != null) {
708 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
709 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
710 RecentActivityExtracterModuleFactory.getModuleName(),
711 downloadedFilePath));
712 long pathID = Util.findID(dataSource, downloadedFilePath);
714 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
715 RecentActivityExtracterModuleFactory.getModuleName(),
718 }
catch (UnsupportedEncodingException ex) {
719 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
723 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
724 RecentActivityExtracterModuleFactory.getModuleName(),
725 Long.valueOf(result.get(
"lastModified").toString())));
726 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
727 RecentActivityExtracterModuleFactory.getModuleName(),
728 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
729 String domain = extractDomain(url);
730 if (domain != null && domain.isEmpty() ==
false) {
731 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
732 RecentActivityExtracterModuleFactory.getModuleName(), domain));
735 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
736 bbartifacts.add(webDownloadArtifact);
739 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
740 FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
741 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
744 }
catch (TskCoreException ex) {
745 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
746 downloadedFilePath), ex);
750 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errParsingArtifacts",
751 this.getDisplayName(), errors));
757 if (!context.dataSourceIngestIsCancelled()) {
758 postArtifacts(bbartifacts);
768 private void getFormsHistory(
long ingestJobId) {
769 FileManager fileManager = currentCase.getServices().getFileManager();
770 List<AbstractFile> formHistoryFiles;
773 Set<String> excludedFieldNames =
new HashSet<>(Arrays.asList(
779 formHistoryFiles = fileManager.findFiles(dataSource,
"formhistory.sqlite",
"Firefox");
780 }
catch (TskCoreException ex) {
781 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.errFetchingFiles");
782 logger.log(Level.WARNING, msg);
783 this.addErrorMessage(this.getDisplayName() +
": " + msg);
787 if (formHistoryFiles.isEmpty()) {
788 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.noFilesFound");
789 logger.log(Level.INFO, msg);
794 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
796 for (AbstractFile formHistoryFile : formHistoryFiles) {
797 if (formHistoryFile.getSize() == 0) {
801 String fileName = formHistoryFile.getName();
802 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
804 ContentUtils.writeToFile(formHistoryFile,
new File(tempFilePath), context::dataSourceIngestIsCancelled);
805 }
catch (ReadContentInputStreamException ex) {
806 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
807 fileName, formHistoryFile.getId()), ex);
808 this.addErrorMessage(
809 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getDisplayName(),
812 }
catch (IOException ex) {
813 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
814 tempFilePath, fileName, formHistoryFile.getId()), ex);
815 this.addErrorMessage(
816 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getDisplayName(),
820 File dbFile =
new File(tempFilePath);
821 if (context.dataSourceIngestIsCancelled()) {
827 boolean isFirefoxV64 = Util.checkColumn(
"timesUsed",
"moz_formhistory", tempFilePath);
828 String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
830 List<HashMap<String, Object>> tempList = this.querySQLiteDb(tempFilePath, formHistoryQuery);
831 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{getDisplayName(), tempFilePath, tempList.size()});
832 for (HashMap<String, Object> result : tempList) {
834 if (context.dataSourceIngestIsCancelled()) {
838 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
840 String fieldName = ((result.get(
"fieldname").toString() != null) ? result.get(
"fieldname").toString() :
"");
842 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
846 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
847 RecentActivityExtracterModuleFactory.getModuleName(),
850 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
851 RecentActivityExtracterModuleFactory.getModuleName(),
852 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
856 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
857 RecentActivityExtracterModuleFactory.getModuleName(),
858 (Long.valueOf(result.get(
"firstUsed").toString()) / 1000000)));
860 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
861 RecentActivityExtracterModuleFactory.getModuleName(),
862 (Long.valueOf(result.get(
"lastUsed").toString()) / 1000000)));
864 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
865 RecentActivityExtracterModuleFactory.getModuleName(),
866 (Integer.valueOf(result.get(
"timesUsed").toString()))));
871 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes));
872 }
catch (TskCoreException ex) {
873 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_FORM_AUTOFILL artifact for file %d", formHistoryFile.getId()), ex);
880 if (!context.dataSourceIngestIsCancelled()) {
881 postArtifacts(bbartifacts);
891 private void getAutofillProfiles(
long ingestJobId) {
892 FileManager fileManager = currentCase.getServices().getFileManager();
893 List<AbstractFile> autofillProfilesFiles;
895 autofillProfilesFiles = fileManager.findFiles(dataSource,
"autofill-profiles.json",
"Firefox");
896 }
catch (TskCoreException ex) {
897 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errGettingFiles");
898 logger.log(Level.SEVERE, msg, ex);
899 this.addErrorMessage(this.getDisplayName() +
": " + msg);
903 if (autofillProfilesFiles.isEmpty()) {
904 logger.log(Level.INFO,
"Didn't find any Firefox Autofill Profiles files.");
910 while (j < autofillProfilesFiles.size()) {
911 AbstractFile profileFile = autofillProfilesFiles.get(j++);
912 if (profileFile.getSize() == 0) {
915 String temps = RAImageIngestModule.getRATempPath(currentCase,
"Firefox", ingestJobId) + File.separator + profileFile.getName() + j +
".json";
917 ContentUtils.writeToFile(profileFile,
new File(temps), context::dataSourceIngestIsCancelled);
918 }
catch (ReadContentInputStreamException ex) {
919 logger.log(Level.WARNING, String.format(
"Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
920 profileFile.getName(), profileFile.getId()), ex);
921 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
922 this.getDisplayName(), profileFile.getName()));
924 }
catch (IOException ex) {
925 logger.log(Level.SEVERE, String.format(
"Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
926 temps, profileFile.getName(), profileFile.getId()), ex);
927 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
928 this.getDisplayName(), profileFile.getName()));
932 logger.log(Level.INFO,
"{0}- Now getting Bookmarks from {1}",
new Object[]{getDisplayName(), temps});
933 File dbFile =
new File(temps);
934 if (context.dataSourceIngestIsCancelled()) {
939 FileReader tempReader;
941 tempReader =
new FileReader(temps);
942 }
catch (FileNotFoundException ex) {
943 logger.log(Level.SEVERE,
"Error while trying to read the Autofill profiles json file for Firefox.", ex);
944 this.addErrorMessage(
945 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getDisplayName(),
946 profileFile.getName()));
950 final JsonParser parser =
new JsonParser();
952 JsonObject jsonRootObject;
953 JsonArray jAddressesArray;
956 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
957 jAddressesArray = jsonRootObject.getAsJsonArray(
"addresses");
958 }
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
959 logger.log(Level.WARNING,
"Error parsing Json for Firefox Autofill profiles.", ex);
960 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
961 this.getDisplayName(), profileFile.getName()));
965 WebBrowserArtifactsHelper helper;
968 helper =
new WebBrowserArtifactsHelper(
969 Case.getCurrentCaseThrows().getSleuthkitCase(),
970 NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName"),
973 }
catch (NoCurrentCaseException ex) {
974 logger.log(Level.SEVERE,
"No case open, bailing.", ex);
978 for (JsonElement result : jAddressesArray) {
979 JsonObject address = result.getAsJsonObject();
980 if (address == null) {
984 JsonElement nameEl = address.get(
"name");
985 String name = (nameEl != null) ? nameEl.getAsString() :
"";
987 JsonElement emailEl = address.get(
"email");
988 String email = (emailEl != null) ? emailEl.getAsString() :
"";
990 JsonElement telEl = address.get(
"tel");
991 String tel = (telEl != null) ? telEl.getAsString() :
"";
992 JsonElement telCountryCodeEl = address.get(
"tel-country-code");
993 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() :
"";
994 JsonElement telNationalEl = address.get(
"tel-national");
995 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() :
"";
997 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
999 JsonElement createdEl = address.get(
"timeCreated");
1000 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong() / 1000 : Long.valueOf(0);
1001 JsonElement lastusedEl = address.get(
"timeLastUsed");
1002 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong() / 1000 : Long.valueOf(0);
1003 JsonElement timesUsedEl = address.get(
"timesUsed");
1004 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
1006 JsonElement addressLine1El = address.get(
"address-line1");
1007 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() :
"";
1008 JsonElement addressLine2El = address.get(
"address-line2");
1009 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() :
"";
1010 JsonElement addressLine3El = address.get(
"address-line3");
1011 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() :
"";
1013 JsonElement postalCodeEl = address.get(
"postal-code");
1014 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() :
"";
1015 JsonElement countryEl = address.get(
"country");
1016 String country = (countryEl != null) ? countryEl.getAsString() :
"";
1018 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country);
1021 helper.addWebFormAddress(name, email, phoneNumber,
1022 mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
1023 }
catch (TskCoreException | Blackboard.BlackboardException ex) {
1024 logger.log(Level.SEVERE,
"Error while trying to insert Firefox Autofill profile artifact{0}", ex);
1025 this.addErrorMessage(
1026 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
1027 this.getDisplayName(), profileFile.getName()));
1042 private String extractDomain(String url) {
1043 if (url == null || url.isEmpty()) {
1047 if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
1054 return NetworkUtils.extractDomain(url);
1068 private String makeTelNumber(String tel, String telCountryCode, String telNational) {
1070 if (tel != null && !tel.isEmpty()) {
1074 if ((telCountryCode != null && !telCountryCode.isEmpty())
1075 && (telNational != null && !telNational.isEmpty())) {
1076 return telCountryCode + telNational;
1093 private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country) {
1094 String fullAddress =
"";
1095 fullAddress = appendAddressField(fullAddress, addressLine1);
1096 fullAddress = appendAddressField(fullAddress, addressLine2);
1097 fullAddress = appendAddressField(fullAddress, addressLine3);
1098 fullAddress = appendAddressField(fullAddress, postalCode);
1099 fullAddress = appendAddressField(fullAddress, country);
1113 private String appendAddressField(String address, String addressfield) {
1115 String updatedAddress = address;
1116 if (addressfield != null && !addressfield.isEmpty()) {
1117 if (!updatedAddress.isEmpty()) {
1118 updatedAddress +=
", ";
1120 updatedAddress += addressfield;
1123 return updatedAddress;