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;
59 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
61 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
63 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
65 import org.
sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
68 "Progress_Message_Firefox_History=Firefox History",
69 "Progress_Message_Firefox_Bookmarks=Firefox Bookmarks",
70 "Progress_Message_Firefox_Cookies=Firefox Cookies",
71 "Progress_Message_Firefox_Downloads=Firefox Downloads",
72 "Progress_Message_Firefox_FormHistory=Firefox Form History",
73 "Progress_Message_Firefox_AutoFill=Firefox Auto Fill"
79 class Firefox extends Extract {
81 private static final Logger logger = Logger.getLogger(Firefox.class.getName());
82 private static final String PLACE_URL_PREFIX =
"place:";
83 private static final String HISTORY_QUERY =
"SELECT moz_historyvisits.id, url, title, visit_count,(visit_date/1000000) AS visit_date,from_visit,"
84 +
"(SELECT url FROM moz_historyvisits history, moz_places places where history.id = moz_historyvisits.from_visit and history.place_id = places.id ) as ref "
85 +
"FROM moz_places, moz_historyvisits "
86 +
"WHERE moz_places.id = moz_historyvisits.place_id "
88 private static final String COOKIE_QUERY =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies";
89 private static final String COOKIE_QUERY_V3 =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies";
90 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";
91 private static final String DOWNLOAD_QUERY =
"SELECT target, source,(startTime/1000000) AS startTime, maxBytes FROM moz_downloads";
92 private static final String DOWNLOAD_QUERY_V24 =
"SELECT url, content AS target, (lastModified/1000000) AS lastModified "
93 +
" FROM moz_places, moz_annos, moz_anno_attributes "
94 +
" WHERE moz_places.id = moz_annos.place_id"
95 +
" AND moz_annos.anno_attribute_id = moz_anno_attributes.id"
96 +
" AND moz_anno_attributes.name='downloads/destinationFileURI'";
97 private static final String FORMHISTORY_QUERY =
"SELECT fieldname, value FROM moz_formhistory";
98 private static final String FORMHISTORY_QUERY_V64 =
"SELECT fieldname, value, timesUsed, firstUsed, lastUsed FROM moz_formhistory";
99 private Content dataSource;
100 private IngestJobContext context;
103 super(NbBundle.getMessage(Firefox.class,
"Firefox.moduleName"));
107 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
108 this.dataSource = dataSource;
109 this.context = context;
111 long ingestJobId = context.getJobId();
113 progressBar.progress(Bundle.Progress_Message_Firefox_History());
114 this.getHistory(context.getJobId());
116 if (context.dataSourceIngestIsCancelled()) {
120 progressBar.progress(Bundle.Progress_Message_Firefox_Bookmarks());
121 this.getBookmark(ingestJobId);
123 if (context.dataSourceIngestIsCancelled()) {
127 progressBar.progress(Bundle.Progress_Message_Firefox_Downloads());
128 this.getDownload(ingestJobId);
130 if (context.dataSourceIngestIsCancelled()) {
134 progressBar.progress(Bundle.Progress_Message_Firefox_Cookies());
135 this.getCookie(ingestJobId);
137 if (context.dataSourceIngestIsCancelled()) {
141 progressBar.progress(Bundle.Progress_Message_Firefox_FormHistory());
142 this.getFormsHistory(ingestJobId);
144 if (context.dataSourceIngestIsCancelled()) {
148 progressBar.progress(Bundle.Progress_Message_Firefox_AutoFill());
149 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.getName() +
": " + 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.getName(),
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.getName(),
206 File dbFile =
new File(temps);
207 if (context.dataSourceIngestIsCancelled()) {
211 List<HashMap<String, Object>> tempList = this.dbConnect(temps, HISTORY_QUERY);
212 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{getName(), 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(ARTIFACT_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);
264 private void getBookmark(
long ingestJobId) {
266 FileManager fileManager = currentCase.getServices().getFileManager();
267 List<AbstractFile> bookmarkFiles;
269 bookmarkFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
270 }
catch (TskCoreException ex) {
271 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getBookmark.errMsg.errFetchFiles");
272 logger.log(Level.WARNING, msg);
273 this.addErrorMessage(this.getName() +
": " + msg);
277 if (bookmarkFiles.isEmpty()) {
278 logger.log(Level.INFO,
"Didn't find any firefox bookmark files.");
283 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
285 for (AbstractFile bookmarkFile : bookmarkFiles) {
286 if (bookmarkFile.getSize() == 0) {
289 String fileName = bookmarkFile.getName();
290 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
292 ContentUtils.writeToFile(bookmarkFile,
new File(temps), context::dataSourceIngestIsCancelled);
293 }
catch (ReadContentInputStreamException ex) {
294 logger.log(Level.WARNING, String.format(
"Error reading Firefox bookmark artifacts file '%s' (id=%d).",
295 fileName, bookmarkFile.getId()), ex);
296 this.addErrorMessage(
297 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
300 }
catch (IOException ex) {
301 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox bookmark artifacts file '%s' (id=%d).",
302 temps, fileName, bookmarkFile.getId()), ex);
303 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getBookmark.errMsg.errAnalyzeFile",
304 this.getName(), fileName));
307 File dbFile =
new File(temps);
308 if (context.dataSourceIngestIsCancelled()) {
312 List<HashMap<String, Object>> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
313 logger.log(Level.INFO,
"{0} - Now getting bookmarks from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
314 for (HashMap<String, Object> result : tempList) {
316 if (context.dataSourceIngestIsCancelled()) {
320 String url = result.get(
"url").toString();
322 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
323 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
324 RecentActivityExtracterModuleFactory.getModuleName(),
325 ((url != null) ? url :
"")));
326 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
327 RecentActivityExtracterModuleFactory.getModuleName(),
328 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
329 if (Long.valueOf(result.get(
"dateAdded").toString()) > 0) {
330 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
331 RecentActivityExtracterModuleFactory.getModuleName(),
332 (Long.valueOf(result.get(
"dateAdded").toString()))));
334 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
335 RecentActivityExtracterModuleFactory.getModuleName(),
336 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
337 String domain = extractDomain(url);
338 if (domain != null && domain.isEmpty() ==
false) {
339 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
340 RecentActivityExtracterModuleFactory.getModuleName(), domain));
344 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes));
345 }
catch (TskCoreException ex) {
346 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_BOOKMARK artifact for file %d", bookmarkFile.getId()), ex);
353 if(!context.dataSourceIngestIsCancelled()) {
354 postArtifacts(bbartifacts);
362 private void getCookie(
long ingestJobId) {
363 FileManager fileManager = currentCase.getServices().getFileManager();
364 List<AbstractFile> cookiesFiles;
366 cookiesFiles = fileManager.findFiles(dataSource,
"cookies.sqlite",
"Firefox");
367 }
catch (TskCoreException ex) {
368 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getCookie.errMsg.errFetchFile");
369 logger.log(Level.WARNING, msg);
370 this.addErrorMessage(this.getName() +
": " + msg);
374 if (cookiesFiles.isEmpty()) {
375 logger.log(Level.INFO,
"Didn't find any Firefox cookie files.");
380 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
382 for (AbstractFile cookiesFile : cookiesFiles) {
383 if (context.dataSourceIngestIsCancelled()) {
387 if (cookiesFile.getSize() == 0) {
390 String fileName = cookiesFile.getName();
391 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
393 ContentUtils.writeToFile(cookiesFile,
new File(temps), context::dataSourceIngestIsCancelled);
394 }
catch (ReadContentInputStreamException ex) {
395 logger.log(Level.WARNING, String.format(
"Error reading Firefox cookie artifacts file '%s' (id=%d).",
396 fileName, cookiesFile.getId()), ex);
397 this.addErrorMessage(
398 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
401 }
catch (IOException ex) {
402 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox cookie artifacts file '%s' (id=%d).",
403 temps, fileName, cookiesFile.getId()), ex);
404 this.addErrorMessage(
405 NbBundle.getMessage(
this.getClass(),
"Firefox.getCookie.errMsg.errAnalyzeFile", this.getName(),
409 File dbFile =
new File(temps);
410 if (context.dataSourceIngestIsCancelled()) {
414 boolean checkColumn = Util.checkColumn(
"creationTime",
"moz_cookies", temps);
417 query = COOKIE_QUERY;
419 query = COOKIE_QUERY_V3;
422 List<HashMap<String, Object>> tempList = this.dbConnect(temps, query);
423 logger.log(Level.INFO,
"{0} - Now getting cookies from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
424 for (HashMap<String, Object> result : tempList) {
426 if (context.dataSourceIngestIsCancelled()) {
430 String host = result.get(
"host").toString();
432 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
433 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
434 RecentActivityExtracterModuleFactory.getModuleName(),
435 ((host != null) ? host :
"")));
436 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
437 RecentActivityExtracterModuleFactory.getModuleName(),
438 (Long.valueOf(result.get(
"lastAccessed").toString()))));
439 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
440 RecentActivityExtracterModuleFactory.getModuleName(),
441 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
442 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
443 RecentActivityExtracterModuleFactory.getModuleName(),
444 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
445 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
446 RecentActivityExtracterModuleFactory.getModuleName(),
447 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
449 if (checkColumn ==
true) {
450 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
451 RecentActivityExtracterModuleFactory.getModuleName(),
452 (Long.valueOf(result.get(
"creationTime").toString()))));
454 String domain = extractDomain(host);
455 if (domain != null && domain.isEmpty() ==
false) {
456 domain = domain.replaceFirst(
"^\\.+(?!$)",
"");
457 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
458 RecentActivityExtracterModuleFactory.getModuleName(), domain));
462 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes));
463 }
catch (TskCoreException ex) {
464 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_COOKIE artifact for file %d", cookiesFile.getId()), ex);
471 if (!context.dataSourceIngestIsCancelled()) {
472 postArtifacts(bbartifacts);
480 private void getDownload(
long ingestJobId) {
481 getDownloadPreVersion24(ingestJobId);
482 getDownloadVersion24(ingestJobId);
491 private void getDownloadPreVersion24(
long ingestJobId) {
493 FileManager fileManager = currentCase.getServices().getFileManager();
494 List<AbstractFile> downloadsFiles;
496 downloadsFiles = fileManager.findFiles(dataSource,
"downloads.sqlite",
"Firefox");
497 }
catch (TskCoreException ex) {
498 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlPre24.errMsg.errFetchFiles");
499 logger.log(Level.WARNING, msg);
500 this.addErrorMessage(this.getName() +
": " + msg);
504 if (downloadsFiles.isEmpty()) {
505 logger.log(Level.INFO,
"Didn't find any pre-version-24.0 Firefox download files.");
510 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
512 for (AbstractFile downloadsFile : downloadsFiles) {
513 if (downloadsFile.getSize() == 0) {
516 String fileName = downloadsFile.getName();
517 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
520 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
521 }
catch (ReadContentInputStreamException ex) {
522 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
523 fileName, downloadsFile.getId()), ex);
524 this.addErrorMessage(
525 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
528 }
catch (IOException ex) {
529 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
530 temps, fileName, downloadsFile.getId()), ex);
531 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errAnalyzeFiles",
532 this.getName(), fileName));
535 File dbFile =
new File(temps);
536 if (context.dataSourceIngestIsCancelled()) {
541 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
542 logger.log(Level.INFO,
"{0}- Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
543 for (HashMap<String, Object> result : tempList) {
545 if (context.dataSourceIngestIsCancelled()) {
549 String source = result.get(
"source").toString();
551 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
553 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
554 RecentActivityExtracterModuleFactory.getModuleName(),
557 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
558 RecentActivityExtracterModuleFactory.getModuleName(),
559 (Long.valueOf(result.get(
"startTime").toString()))));
561 String target = result.get(
"target").toString();
562 String downloadedFilePath =
"";
563 if (target != null) {
565 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
566 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
567 RecentActivityExtracterModuleFactory.getModuleName(),
568 downloadedFilePath));
569 long pathID = Util.findID(dataSource, downloadedFilePath);
571 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
572 RecentActivityExtracterModuleFactory.getModuleName(),
575 }
catch (UnsupportedEncodingException ex) {
576 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
581 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
582 RecentActivityExtracterModuleFactory.getModuleName(),
583 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
584 String domain = extractDomain(source);
585 if (domain != null && domain.isEmpty() ==
false) {
586 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
587 RecentActivityExtracterModuleFactory.getModuleName(),
591 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
592 bbartifacts.add(webDownloadArtifact);
595 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
596 FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
597 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
600 }
catch (TskCoreException ex) {
601 logger.log(Level.SEVERE, String.format(
"Error creating TSK_WEB_DOWNLOAD or TSK_ASSOCIATED_ARTIFACT artifact for file '%d'",
602 downloadsFile.getId()), ex);
607 this.addErrorMessage(
608 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errParsingArtifacts",
609 this.getName(), errors));
615 if(!context.dataSourceIngestIsCancelled()) {
616 postArtifacts(bbartifacts);
626 private void getDownloadVersion24(
long ingestJobId) {
627 FileManager fileManager = currentCase.getServices().getFileManager();
628 List<AbstractFile> downloadsFiles;
630 downloadsFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
631 }
catch (TskCoreException ex) {
632 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlV24.errMsg.errFetchFiles");
633 logger.log(Level.WARNING, msg);
634 this.addErrorMessage(this.getName() +
": " + msg);
638 if (downloadsFiles.isEmpty()) {
639 logger.log(Level.INFO,
"Didn't find any version-24.0 Firefox download files.");
644 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
646 for (AbstractFile downloadsFile : downloadsFiles) {
647 if (downloadsFile.getSize() == 0) {
650 String fileName = downloadsFile.getName();
651 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName +
"-downloads" + j +
".db";
654 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
655 }
catch (ReadContentInputStreamException ex) {
656 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
657 fileName, downloadsFile.getId()), ex);
658 this.addErrorMessage(
659 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
662 }
catch (IOException ex) {
663 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
664 temps, fileName, downloadsFile.getId()), ex);
665 this.addErrorMessage(
666 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errAnalyzeFile", this.getName(),
670 File dbFile =
new File(temps);
671 if (context.dataSourceIngestIsCancelled()) {
676 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V24);
678 logger.log(Level.INFO,
"{0} - Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
679 for (HashMap<String, Object> result : tempList) {
681 if (context.dataSourceIngestIsCancelled()) {
685 String url = result.get(
"url").toString();
687 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
689 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
690 RecentActivityExtracterModuleFactory.getModuleName(),
696 String target = result.get(
"target").toString();
697 String downloadedFilePath =
"";
698 if (target != null) {
700 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
701 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
702 RecentActivityExtracterModuleFactory.getModuleName(),
703 downloadedFilePath));
704 long pathID = Util.findID(dataSource, downloadedFilePath);
706 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
707 RecentActivityExtracterModuleFactory.getModuleName(),
710 }
catch (UnsupportedEncodingException ex) {
711 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
715 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
716 RecentActivityExtracterModuleFactory.getModuleName(),
717 Long.valueOf(result.get(
"lastModified").toString())));
718 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
719 RecentActivityExtracterModuleFactory.getModuleName(),
720 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
721 String domain = extractDomain(url);
722 if (domain != null && domain.isEmpty() ==
false) {
723 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
724 RecentActivityExtracterModuleFactory.getModuleName(), domain));
727 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
728 bbartifacts.add(webDownloadArtifact);
731 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
732 FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
733 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
736 }
catch (TskCoreException ex) {
737 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
738 downloadedFilePath), ex);
742 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errParsingArtifacts",
743 this.getName(), errors));
749 if(!context.dataSourceIngestIsCancelled()) {
750 postArtifacts(bbartifacts);
759 private void getFormsHistory(
long ingestJobId) {
760 FileManager fileManager = currentCase.getServices().getFileManager();
761 List<AbstractFile> formHistoryFiles;
764 Set<String> excludedFieldNames =
new HashSet<>(Arrays.asList(
770 formHistoryFiles = fileManager.findFiles(dataSource,
"formhistory.sqlite",
"Firefox");
771 }
catch (TskCoreException ex) {
772 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.errFetchingFiles");
773 logger.log(Level.WARNING, msg);
774 this.addErrorMessage(this.getName() +
": " + msg);
778 if (formHistoryFiles.isEmpty()) {
779 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.noFilesFound");
780 logger.log(Level.INFO, msg);
785 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
787 for (AbstractFile formHistoryFile : formHistoryFiles) {
788 if (formHistoryFile.getSize() == 0) {
792 String fileName = formHistoryFile.getName();
793 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase,
"firefox", ingestJobId) + File.separator + fileName + j +
".db";
795 ContentUtils.writeToFile(formHistoryFile,
new File(tempFilePath), context::dataSourceIngestIsCancelled);
796 }
catch (ReadContentInputStreamException ex) {
797 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
798 fileName, formHistoryFile.getId()), ex);
799 this.addErrorMessage(
800 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
803 }
catch (IOException ex) {
804 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
805 tempFilePath, fileName, formHistoryFile.getId()), ex);
806 this.addErrorMessage(
807 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
811 File dbFile =
new File(tempFilePath);
812 if (context.dataSourceIngestIsCancelled()) {
818 boolean isFirefoxV64 = Util.checkColumn(
"timesUsed",
"moz_formhistory", tempFilePath);
819 String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
821 List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
822 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{getName(), tempFilePath, tempList.size()});
823 for (HashMap<String, Object> result : tempList) {
825 if (context.dataSourceIngestIsCancelled()) {
829 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
831 String fieldName = ((result.get(
"fieldname").toString() != null) ? result.get(
"fieldname").toString() :
"");
833 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
837 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
838 RecentActivityExtracterModuleFactory.getModuleName(),
841 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
842 RecentActivityExtracterModuleFactory.getModuleName(),
843 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
847 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
848 RecentActivityExtracterModuleFactory.getModuleName(),
849 (Long.valueOf(result.get(
"firstUsed").toString()) / 1000000)));
851 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
852 RecentActivityExtracterModuleFactory.getModuleName(),
853 (Long.valueOf(result.get(
"lastUsed").toString()) / 1000000)));
855 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
856 RecentActivityExtracterModuleFactory.getModuleName(),
857 (Integer.valueOf(result.get(
"timesUsed").toString()))));
862 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes));
863 }
catch (TskCoreException ex) {
864 logger.log(Level.SEVERE, String.format(
"Failed to create TSK_WEB_FORM_AUTOFILL artifact for file %d", formHistoryFile.getId()), ex);
871 if(!context.dataSourceIngestIsCancelled()) {
872 postArtifacts(bbartifacts);
882 private void getAutofillProfiles(
long ingestJobId) {
883 FileManager fileManager = currentCase.getServices().getFileManager();
884 List<AbstractFile> autofillProfilesFiles;
886 autofillProfilesFiles = fileManager.findFiles(dataSource,
"autofill-profiles.json",
"Firefox");
887 }
catch (TskCoreException ex) {
888 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errGettingFiles");
889 logger.log(Level.SEVERE, msg, ex);
890 this.addErrorMessage(this.getName() +
": " + msg);
894 if (autofillProfilesFiles.isEmpty()) {
895 logger.log(Level.INFO,
"Didn't find any Firefox Autofill Profiles files.");
902 while (j < autofillProfilesFiles.size()) {
903 AbstractFile profileFile = autofillProfilesFiles.get(j++);
904 if (profileFile.getSize() == 0) {
907 String temps = RAImageIngestModule.getRATempPath(currentCase,
"Firefox", ingestJobId) + File.separator + profileFile.getName() + j +
".json";
909 ContentUtils.writeToFile(profileFile,
new File(temps), context::dataSourceIngestIsCancelled);
910 }
catch (ReadContentInputStreamException ex) {
911 logger.log(Level.WARNING, String.format(
"Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
912 profileFile.getName(), profileFile.getId()), ex);
913 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
914 this.getName(), profileFile.getName()));
916 }
catch (IOException ex) {
917 logger.log(Level.SEVERE, String.format(
"Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
918 temps, profileFile.getName(), profileFile.getId()), ex);
919 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
920 this.getName(), profileFile.getName()));
924 logger.log(Level.INFO,
"{0}- Now getting Bookmarks from {1}",
new Object[]{getName(), temps});
925 File dbFile =
new File(temps);
926 if (context.dataSourceIngestIsCancelled()) {
931 FileReader tempReader;
933 tempReader =
new FileReader(temps);
934 }
catch (FileNotFoundException ex) {
935 logger.log(Level.SEVERE,
"Error while trying to read the Autofill profiles json file for Firefox.", ex);
936 this.addErrorMessage(
937 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
938 profileFile.getName()));
942 final JsonParser parser =
new JsonParser();
944 JsonObject jsonRootObject;
945 JsonArray jAddressesArray;
948 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
949 jAddressesArray = jsonRootObject.getAsJsonArray(
"addresses");
950 }
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
951 logger.log(Level.WARNING,
"Error parsing Json for Firefox Autofill profiles.", ex);
952 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
953 this.getName(), profileFile.getName()));
957 WebBrowserArtifactsHelper helper;
960 helper =
new WebBrowserArtifactsHelper(
961 Case.getCurrentCaseThrows().getSleuthkitCase(),
962 NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName"),
965 }
catch (NoCurrentCaseException ex) {
966 logger.log(Level.SEVERE,
"No case open, bailing.", ex);
970 for (JsonElement result : jAddressesArray) {
971 JsonObject address = result.getAsJsonObject();
972 if (address == null) {
976 JsonElement nameEl = address.get(
"name");
977 String name = (nameEl != null) ? nameEl.getAsString() :
"";
979 JsonElement emailEl = address.get(
"email");
980 String email = (emailEl != null) ? emailEl.getAsString() :
"";
982 JsonElement telEl = address.get(
"tel");
983 String tel = (telEl != null) ? telEl.getAsString() :
"";
984 JsonElement telCountryCodeEl = address.get(
"tel-country-code");
985 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() :
"";
986 JsonElement telNationalEl = address.get(
"tel-national");
987 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() :
"";
989 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
991 JsonElement createdEl = address.get(
"timeCreated");
992 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);
993 JsonElement lastusedEl = address.get(
"timeLastUsed");
994 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0);
995 JsonElement timesUsedEl = address.get(
"timesUsed");
996 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
998 JsonElement addressLine1El = address.get(
"address-line1");
999 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() :
"";
1000 JsonElement addressLine2El = address.get(
"address-line2");
1001 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() :
"";
1002 JsonElement addressLine3El = address.get(
"address-line3");
1003 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() :
"";
1005 JsonElement postalCodeEl = address.get(
"postal-code");
1006 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() :
"";
1007 JsonElement countryEl = address.get(
"country");
1008 String country = (countryEl != null) ? countryEl.getAsString() :
"";
1010 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
1013 helper.addWebFormAddress(name, email, phoneNumber,
1014 mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
1015 }
catch (TskCoreException | Blackboard.BlackboardException ex) {
1016 logger.log(Level.SEVERE,
"Error while trying to insert Firefox Autofill profile artifact{0}", ex);
1017 this.addErrorMessage(
1018 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
1019 this.getName(), profileFile.getName()));
1034 private String extractDomain(String url) {
1035 if (url == null || url.isEmpty()) {
1039 if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
1046 return NetworkUtils.extractDomain(url);
1059 private String makeTelNumber(String tel, String telCountryCode, String telNational) {
1061 if (tel != null && !tel.isEmpty()) {
1065 if ((telCountryCode != null && !telCountryCode.isEmpty()) &&
1066 (telNational != null && !telNational.isEmpty())) {
1067 return telCountryCode + telNational;
1084 private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country ) {
1085 String fullAddress =
"";
1086 fullAddress = appendAddressField(fullAddress, addressLine1 );
1087 fullAddress = appendAddressField(fullAddress, addressLine2 );
1088 fullAddress = appendAddressField(fullAddress, addressLine3 );
1089 fullAddress = appendAddressField(fullAddress, postalCode );
1090 fullAddress = appendAddressField(fullAddress, country );
1103 private String appendAddressField(String address, String addressfield) {
1105 String updatedAddress = address;
1106 if (addressfield != null && !addressfield.isEmpty()) {
1107 if (!updatedAddress.isEmpty()) {
1108 updatedAddress +=
", ";
1110 updatedAddress += addressfield;
1113 return updatedAddress;