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;
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 IngestJobContext context;
102 moduleName = NbBundle.getMessage(Firefox.class,
"Firefox.moduleName");
106 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
107 this.dataSource = dataSource;
108 this.context = context;
111 progressBar.progress(Bundle.Progress_Message_Firefox_History());
114 progressBar.progress(Bundle.Progress_Message_Firefox_Bookmarks());
117 progressBar.progress(Bundle.Progress_Message_Firefox_Downloads());
120 progressBar.progress(Bundle.Progress_Message_Firefox_Cookies());
123 progressBar.progress(Bundle.Progress_Message_Firefox_FormHistory());
124 this.getFormsHistory();
126 progressBar.progress(Bundle.Progress_Message_Firefox_AutoFill());
127 this.getAutofillProfiles();
130 private void getHistory() {
131 FileManager fileManager = currentCase.getServices().getFileManager();
132 List<AbstractFile> historyFiles;
134 historyFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
135 }
catch (TskCoreException ex) {
136 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.errFetchingFiles");
137 logger.log(Level.WARNING, msg);
138 this.addErrorMessage(this.getName() +
": " + msg);
142 if (historyFiles.isEmpty()) {
143 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.noFilesFound");
144 logger.log(Level.INFO, msg);
149 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
151 for (AbstractFile historyFile : historyFiles) {
153 if (context.dataSourceIngestIsCancelled()) {
157 if (historyFile.getSize() == 0) {
161 String fileName = historyFile.getName();
162 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
164 ContentUtils.writeToFile(historyFile,
new File(temps), context::dataSourceIngestIsCancelled);
165 }
catch (ReadContentInputStreamException ex) {
166 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
167 fileName, historyFile.getId()), ex);
168 this.addErrorMessage(
169 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
172 }
catch (IOException ex) {
173 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
174 temps, fileName, historyFile.getId()), ex);
175 this.addErrorMessage(
176 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
180 File dbFile =
new File(temps);
181 if (context.dataSourceIngestIsCancelled()) {
185 List<HashMap<String, Object>> tempList = this.dbConnect(temps, HISTORY_QUERY);
186 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
187 for (HashMap<String, Object> result : tempList) {
189 if (context.dataSourceIngestIsCancelled()) {
193 String url = result.get(
"url").toString();
195 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
196 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
197 RecentActivityExtracterModuleFactory.getModuleName(),
198 ((url != null) ? url :
"")));
200 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
201 RecentActivityExtracterModuleFactory.getModuleName(),
202 (Long.valueOf(result.get(
"visit_date").toString()))));
203 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
204 RecentActivityExtracterModuleFactory.getModuleName(),
205 ((result.get(
"ref").toString() != null) ? result.get(
"ref").toString() :
"")));
206 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
207 RecentActivityExtracterModuleFactory.getModuleName(),
208 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
209 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
210 RecentActivityExtracterModuleFactory.getModuleName(),
211 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
212 String domain = extractDomain(url);
213 if (domain != null && domain.isEmpty() ==
false) {
214 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
215 RecentActivityExtracterModuleFactory.getModuleName(), domain));
218 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
220 bbartifacts.add(bbart);
227 postArtifacts(bbartifacts);
233 private void getBookmark() {
235 FileManager fileManager = currentCase.getServices().getFileManager();
236 List<AbstractFile> bookmarkFiles;
238 bookmarkFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
239 }
catch (TskCoreException ex) {
240 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getBookmark.errMsg.errFetchFiles");
241 logger.log(Level.WARNING, msg);
242 this.addErrorMessage(this.getName() +
": " + msg);
246 if (bookmarkFiles.isEmpty()) {
247 logger.log(Level.INFO,
"Didn't find any firefox bookmark files.");
252 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
254 for (AbstractFile bookmarkFile : bookmarkFiles) {
255 if (bookmarkFile.getSize() == 0) {
258 String fileName = bookmarkFile.getName();
259 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
261 ContentUtils.writeToFile(bookmarkFile,
new File(temps), context::dataSourceIngestIsCancelled);
262 }
catch (ReadContentInputStreamException ex) {
263 logger.log(Level.WARNING, String.format(
"Error reading Firefox bookmark artifacts file '%s' (id=%d).",
264 fileName, bookmarkFile.getId()), ex);
265 this.addErrorMessage(
266 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
269 }
catch (IOException ex) {
270 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox bookmark artifacts file '%s' (id=%d).",
271 temps, fileName, bookmarkFile.getId()), ex);
272 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getBookmark.errMsg.errAnalyzeFile",
273 this.getName(), fileName));
276 File dbFile =
new File(temps);
277 if (context.dataSourceIngestIsCancelled()) {
281 List<HashMap<String, Object>> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
282 logger.log(Level.INFO,
"{0} - Now getting bookmarks from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
283 for (HashMap<String, Object> result : tempList) {
285 if (context.dataSourceIngestIsCancelled()) {
289 String url = result.get(
"url").toString();
291 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
292 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
293 RecentActivityExtracterModuleFactory.getModuleName(),
294 ((url != null) ? url :
"")));
295 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
296 RecentActivityExtracterModuleFactory.getModuleName(),
297 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
298 if (Long.valueOf(result.get(
"dateAdded").toString()) > 0) {
299 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
300 RecentActivityExtracterModuleFactory.getModuleName(),
301 (Long.valueOf(result.get(
"dateAdded").toString()))));
303 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
304 RecentActivityExtracterModuleFactory.getModuleName(),
305 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
306 String domain = extractDomain(url);
307 if (domain != null && domain.isEmpty() ==
false) {
308 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
309 RecentActivityExtracterModuleFactory.getModuleName(), domain));
312 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes);
314 bbartifacts.add(bbart);
321 postArtifacts(bbartifacts);
327 private void getCookie() {
328 FileManager fileManager = currentCase.getServices().getFileManager();
329 List<AbstractFile> cookiesFiles;
331 cookiesFiles = fileManager.findFiles(dataSource,
"cookies.sqlite",
"Firefox");
332 }
catch (TskCoreException ex) {
333 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getCookie.errMsg.errFetchFile");
334 logger.log(Level.WARNING, msg);
335 this.addErrorMessage(this.getName() +
": " + msg);
339 if (cookiesFiles.isEmpty()) {
340 logger.log(Level.INFO,
"Didn't find any Firefox cookie files.");
345 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
347 for (AbstractFile cookiesFile : cookiesFiles) {
348 if (cookiesFile.getSize() == 0) {
351 String fileName = cookiesFile.getName();
352 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
354 ContentUtils.writeToFile(cookiesFile,
new File(temps), context::dataSourceIngestIsCancelled);
355 }
catch (ReadContentInputStreamException ex) {
356 logger.log(Level.WARNING, String.format(
"Error reading Firefox cookie artifacts file '%s' (id=%d).",
357 fileName, cookiesFile.getId()), ex);
358 this.addErrorMessage(
359 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
362 }
catch (IOException ex) {
363 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox cookie artifacts file '%s' (id=%d).",
364 temps, fileName, cookiesFile.getId()), ex);
365 this.addErrorMessage(
366 NbBundle.getMessage(
this.getClass(),
"Firefox.getCookie.errMsg.errAnalyzeFile", this.getName(),
370 File dbFile =
new File(temps);
371 if (context.dataSourceIngestIsCancelled()) {
375 boolean checkColumn = Util.checkColumn(
"creationTime",
"moz_cookies", temps);
378 query = COOKIE_QUERY;
380 query = COOKIE_QUERY_V3;
383 List<HashMap<String, Object>> tempList = this.dbConnect(temps, query);
384 logger.log(Level.INFO,
"{0} - Now getting cookies from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
385 for (HashMap<String, Object> result : tempList) {
387 if (context.dataSourceIngestIsCancelled()) {
391 String host = result.get(
"host").toString();
393 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
394 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
395 RecentActivityExtracterModuleFactory.getModuleName(),
396 ((host != null) ? host :
"")));
397 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
398 RecentActivityExtracterModuleFactory.getModuleName(),
399 (Long.valueOf(result.get(
"lastAccessed").toString()))));
400 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
401 RecentActivityExtracterModuleFactory.getModuleName(),
402 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
403 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
404 RecentActivityExtracterModuleFactory.getModuleName(),
405 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
406 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
407 RecentActivityExtracterModuleFactory.getModuleName(),
408 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
410 if (checkColumn ==
true) {
411 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
412 RecentActivityExtracterModuleFactory.getModuleName(),
413 (Long.valueOf(result.get(
"creationTime").toString()))));
415 String domain = extractDomain(host);
416 if (domain != null && domain.isEmpty() ==
false) {
417 domain = domain.replaceFirst(
"^\\.+(?!$)",
"");
418 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
419 RecentActivityExtracterModuleFactory.getModuleName(), domain));
422 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
424 bbartifacts.add(bbart);
431 postArtifacts(bbartifacts);
437 private void getDownload() {
438 getDownloadPreVersion24();
439 getDownloadVersion24();
447 private void getDownloadPreVersion24() {
449 FileManager fileManager = currentCase.getServices().getFileManager();
450 List<AbstractFile> downloadsFiles;
452 downloadsFiles = fileManager.findFiles(dataSource,
"downloads.sqlite",
"Firefox");
453 }
catch (TskCoreException ex) {
454 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlPre24.errMsg.errFetchFiles");
455 logger.log(Level.WARNING, msg);
456 this.addErrorMessage(this.getName() +
": " + msg);
460 if (downloadsFiles.isEmpty()) {
461 logger.log(Level.INFO,
"Didn't find any pre-version-24.0 Firefox download files.");
466 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
468 for (AbstractFile downloadsFile : downloadsFiles) {
469 if (downloadsFile.getSize() == 0) {
472 String fileName = downloadsFile.getName();
473 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
476 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
477 }
catch (ReadContentInputStreamException ex) {
478 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
479 fileName, downloadsFile.getId()), ex);
480 this.addErrorMessage(
481 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
484 }
catch (IOException ex) {
485 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
486 temps, fileName, downloadsFile.getId()), ex);
487 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errAnalyzeFiles",
488 this.getName(), fileName));
491 File dbFile =
new File(temps);
492 if (context.dataSourceIngestIsCancelled()) {
497 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
498 logger.log(Level.INFO,
"{0}- Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
499 for (HashMap<String, Object> result : tempList) {
501 if (context.dataSourceIngestIsCancelled()) {
505 String source = result.get(
"source").toString();
507 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
509 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
510 RecentActivityExtracterModuleFactory.getModuleName(),
513 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
514 RecentActivityExtracterModuleFactory.getModuleName(),
515 (Long.valueOf(result.get(
"startTime").toString()))));
517 String target = result.get(
"target").toString();
518 String downloadedFilePath =
"";
519 if (target != null) {
521 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
522 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
523 RecentActivityExtracterModuleFactory.getModuleName(),
524 downloadedFilePath));
525 long pathID = Util.findID(dataSource, downloadedFilePath);
527 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
528 RecentActivityExtracterModuleFactory.getModuleName(),
531 }
catch (UnsupportedEncodingException ex) {
532 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
537 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
538 RecentActivityExtracterModuleFactory.getModuleName(),
539 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
540 String domain = extractDomain(source);
541 if (domain != null && domain.isEmpty() ==
false) {
542 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
543 RecentActivityExtracterModuleFactory.getModuleName(),
547 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
548 if (webDownloadArtifact != null) {
549 bbartifacts.add(webDownloadArtifact);
553 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
554 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
555 associatedObjectArtifact.addAttribute(
556 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
557 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
559 bbartifacts.add(associatedObjectArtifact);
562 }
catch (TskCoreException ex) {
563 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
564 downloadedFilePath), ex);
569 this.addErrorMessage(
570 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errParsingArtifacts",
571 this.getName(), errors));
578 postArtifacts(bbartifacts);
586 private void getDownloadVersion24() {
587 FileManager fileManager = currentCase.getServices().getFileManager();
588 List<AbstractFile> downloadsFiles;
590 downloadsFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
591 }
catch (TskCoreException ex) {
592 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlV24.errMsg.errFetchFiles");
593 logger.log(Level.WARNING, msg);
594 this.addErrorMessage(this.getName() +
": " + msg);
598 if (downloadsFiles.isEmpty()) {
599 logger.log(Level.INFO,
"Didn't find any version-24.0 Firefox download files.");
604 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
606 for (AbstractFile downloadsFile : downloadsFiles) {
607 if (downloadsFile.getSize() == 0) {
610 String fileName = downloadsFile.getName();
611 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName +
"-downloads" + j +
".db";
614 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
615 }
catch (ReadContentInputStreamException ex) {
616 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
617 fileName, downloadsFile.getId()), ex);
618 this.addErrorMessage(
619 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
622 }
catch (IOException ex) {
623 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
624 temps, fileName, downloadsFile.getId()), ex);
625 this.addErrorMessage(
626 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errAnalyzeFile", this.getName(),
630 File dbFile =
new File(temps);
631 if (context.dataSourceIngestIsCancelled()) {
636 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V24);
638 logger.log(Level.INFO,
"{0} - Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
639 for (HashMap<String, Object> result : tempList) {
641 if (context.dataSourceIngestIsCancelled()) {
645 String url = result.get(
"url").toString();
647 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
649 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
650 RecentActivityExtracterModuleFactory.getModuleName(),
656 String target = result.get(
"target").toString();
657 String downloadedFilePath =
"";
658 if (target != null) {
660 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
661 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
662 RecentActivityExtracterModuleFactory.getModuleName(),
663 downloadedFilePath));
664 long pathID = Util.findID(dataSource, downloadedFilePath);
666 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
667 RecentActivityExtracterModuleFactory.getModuleName(),
670 }
catch (UnsupportedEncodingException ex) {
671 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
675 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
676 RecentActivityExtracterModuleFactory.getModuleName(),
677 Long.valueOf(result.get(
"lastModified").toString())));
678 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
679 RecentActivityExtracterModuleFactory.getModuleName(),
680 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
681 String domain = extractDomain(url);
682 if (domain != null && domain.isEmpty() ==
false) {
683 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
684 RecentActivityExtracterModuleFactory.getModuleName(), domain));
687 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
688 if (webDownloadArtifact != null) {
689 bbartifacts.add(webDownloadArtifact);
693 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
694 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
695 associatedObjectArtifact.addAttribute(
696 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
697 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
698 bbartifacts.add(associatedObjectArtifact);
701 }
catch (TskCoreException ex) {
702 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
703 downloadedFilePath), ex);
708 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errParsingArtifacts",
709 this.getName(), errors));
716 postArtifacts(bbartifacts);
723 private void getFormsHistory() {
724 FileManager fileManager = currentCase.getServices().getFileManager();
725 List<AbstractFile> formHistoryFiles;
728 Set<String> excludedFieldNames =
new HashSet<>(Arrays.asList(
734 formHistoryFiles = fileManager.findFiles(dataSource,
"formhistory.sqlite",
"Firefox");
735 }
catch (TskCoreException ex) {
736 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.errFetchingFiles");
737 logger.log(Level.WARNING, msg);
738 this.addErrorMessage(this.getName() +
": " + msg);
742 if (formHistoryFiles.isEmpty()) {
743 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.noFilesFound");
744 logger.log(Level.INFO, msg);
749 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
751 for (AbstractFile formHistoryFile : formHistoryFiles) {
752 if (formHistoryFile.getSize() == 0) {
756 String fileName = formHistoryFile.getName();
757 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
759 ContentUtils.writeToFile(formHistoryFile,
new File(tempFilePath), context::dataSourceIngestIsCancelled);
760 }
catch (ReadContentInputStreamException ex) {
761 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
762 fileName, formHistoryFile.getId()), ex);
763 this.addErrorMessage(
764 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
767 }
catch (IOException ex) {
768 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
769 tempFilePath, fileName, formHistoryFile.getId()), ex);
770 this.addErrorMessage(
771 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
775 File dbFile =
new File(tempFilePath);
776 if (context.dataSourceIngestIsCancelled()) {
782 boolean isFirefoxV64 = Util.checkColumn(
"timesUsed",
"moz_formhistory", tempFilePath);
783 String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
785 List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
786 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{moduleName, tempFilePath, tempList.size()});
787 for (HashMap<String, Object> result : tempList) {
789 if (context.dataSourceIngestIsCancelled()) {
793 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
795 String fieldName = ((result.get(
"fieldname").toString() != null) ? result.get(
"fieldname").toString() :
"");
797 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
801 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
802 RecentActivityExtracterModuleFactory.getModuleName(),
805 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
806 RecentActivityExtracterModuleFactory.getModuleName(),
807 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
811 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
812 RecentActivityExtracterModuleFactory.getModuleName(),
813 (Long.valueOf(result.get(
"firstUsed").toString()) / 1000000)));
815 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
816 RecentActivityExtracterModuleFactory.getModuleName(),
817 (Long.valueOf(result.get(
"lastUsed").toString()) / 1000000)));
819 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
820 RecentActivityExtracterModuleFactory.getModuleName(),
821 (Integer.valueOf(result.get(
"timesUsed").toString()))));
825 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes);
827 bbartifacts.add(bbart);
834 postArtifacts(bbartifacts);
843 private void getAutofillProfiles() {
844 FileManager fileManager = currentCase.getServices().getFileManager();
845 List<AbstractFile> autofillProfilesFiles;
847 autofillProfilesFiles = fileManager.findFiles(dataSource,
"autofill-profiles.json",
"Firefox");
848 }
catch (TskCoreException ex) {
849 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errGettingFiles");
850 logger.log(Level.SEVERE, msg, ex);
851 this.addErrorMessage(this.getName() +
": " + msg);
855 if (autofillProfilesFiles.isEmpty()) {
856 logger.log(Level.INFO,
"Didn't find any Firefox Autofill Profiles files.");
861 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
864 while (j < autofillProfilesFiles.size()) {
865 AbstractFile profileFile = autofillProfilesFiles.get(j++);
866 if (profileFile.getSize() == 0) {
869 String temps = RAImageIngestModule.getRATempPath(currentCase,
"Firefox") + File.separator + profileFile.getName() + j +
".json";
871 ContentUtils.writeToFile(profileFile,
new File(temps), context::dataSourceIngestIsCancelled);
872 }
catch (ReadContentInputStreamException ex) {
873 logger.log(Level.WARNING, String.format(
"Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
874 profileFile.getName(), profileFile.getId()), ex);
875 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
876 this.getName(), profileFile.getName()));
878 }
catch (IOException ex) {
879 logger.log(Level.SEVERE, String.format(
"Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
880 temps, profileFile.getName(), profileFile.getId()), ex);
881 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
882 this.getName(), profileFile.getName()));
886 logger.log(Level.INFO,
"{0}- Now getting Bookmarks from {1}",
new Object[]{moduleName, temps});
887 File dbFile =
new File(temps);
888 if (context.dataSourceIngestIsCancelled()) {
893 FileReader tempReader;
895 tempReader =
new FileReader(temps);
896 }
catch (FileNotFoundException ex) {
897 logger.log(Level.SEVERE,
"Error while trying to read the Autofill profiles json file for Firefox.", ex);
898 this.addErrorMessage(
899 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
900 profileFile.getName()));
904 final JsonParser parser =
new JsonParser();
906 JsonObject jsonRootObject;
907 JsonArray jAddressesArray;
910 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
911 jAddressesArray = jsonRootObject.getAsJsonArray(
"addresses");
912 }
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
913 logger.log(Level.WARNING,
"Error parsing Json for Firefox Autofill profiles.", ex);
914 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
915 this.getName(), profileFile.getName()));
919 for (JsonElement result : jAddressesArray) {
920 JsonObject address = result.getAsJsonObject();
921 if (address == null) {
925 JsonElement nameEl = address.get(
"name");
926 String name = (nameEl != null) ? nameEl.getAsString() :
"";
928 JsonElement emailEl = address.get(
"email");
929 String email = (emailEl != null) ? emailEl.getAsString() :
"";
931 JsonElement telEl = address.get(
"tel");
932 String tel = (telEl != null) ? telEl.getAsString() :
"";
933 JsonElement telCountryCodeEl = address.get(
"tel-country-code");
934 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() :
"";
935 JsonElement telNationalEl = address.get(
"tel-national");
936 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() :
"";
938 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
940 JsonElement createdEl = address.get(
"timeCreated");
941 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);
942 JsonElement lastusedEl = address.get(
"timeLastUsed");
943 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0);
944 JsonElement timesUsedEl = address.get(
"timesUsed");
945 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
947 JsonElement addressLine1El = address.get(
"address-line1");
948 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() :
"";
949 JsonElement addressLine2El = address.get(
"address-line2");
950 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() :
"";
951 JsonElement addressLine3El = address.get(
"address-line3");
952 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() :
"";
954 JsonElement postalCodeEl = address.get(
"postal-code");
955 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() :
"";
956 JsonElement countryEl = address.get(
"country");
957 String country = (countryEl != null) ? countryEl.getAsString() :
"";
959 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
962 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
963 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
964 RecentActivityExtracterModuleFactory.getModuleName(),
967 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
968 RecentActivityExtracterModuleFactory.getModuleName(),
971 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
972 RecentActivityExtracterModuleFactory.getModuleName(),
975 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
976 RecentActivityExtracterModuleFactory.getModuleName(),
979 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
980 RecentActivityExtracterModuleFactory.getModuleName(),
983 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
984 RecentActivityExtracterModuleFactory.getModuleName(),
987 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
988 RecentActivityExtracterModuleFactory.getModuleName(),
991 BlackboardArtifact bbart = profileFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS);
994 bbart.addAttributes(bbattributes);
995 bbartifacts.add(bbart);
999 if (email != null && !email.isEmpty()) {
1001 Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email, NbBundle.getMessage(
this.getClass(),
"Firefox.parentModuleName"), profileFile);
1002 }
catch (NoCurrentCaseException | TskCoreException ex) {
1003 logger.log(Level.SEVERE, String.format(
"Error creating email account instance for '%s' from Firefox profiles file '%s' .",
1004 email, profileFile.getName()), ex);
1009 if (phoneNumber != null && !phoneNumber.isEmpty()) {
1011 Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phoneNumber, NbBundle.getMessage(
this.getClass(),
"Firefox.parentModuleName"), profileFile);
1012 }
catch (NoCurrentCaseException | TskCoreException ex) {
1013 logger.log(Level.SEVERE, String.format(
"Error creating phone number account instance for '%s' from Chrome profiles file '%s' .",
1014 phoneNumber, profileFile.getName()), ex);
1018 }
catch (TskCoreException ex) {
1019 logger.log(Level.SEVERE,
"Error while trying to insert Firefox Autofill profile artifact{0}", ex);
1020 this.addErrorMessage(
1021 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
1022 this.getName(), profileFile.getName()));
1028 postArtifacts(bbartifacts);
1039 private String extractDomain(String url) {
1040 if (url == null || url.isEmpty()) {
1044 if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
1051 return NetworkUtils.extractDomain(url);
1064 private String makeTelNumber(String tel, String telCountryCode, String telNational) {
1066 if (tel != null && !tel.isEmpty()) {
1070 if ((telCountryCode != null && !telCountryCode.isEmpty()) &&
1071 (telNational != null && !telNational.isEmpty())) {
1072 return telCountryCode + telNational;
1089 private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country ) {
1090 String fullAddress =
"";
1091 fullAddress = appendAddressField(fullAddress, addressLine1 );
1092 fullAddress = appendAddressField(fullAddress, addressLine2 );
1093 fullAddress = appendAddressField(fullAddress, addressLine3 );
1094 fullAddress = appendAddressField(fullAddress, postalCode );
1095 fullAddress = appendAddressField(fullAddress, country );
1108 private String appendAddressField(String address, String addressfield) {
1110 String updatedAddress = address;
1111 if (addressfield != null && !addressfield.isEmpty()) {
1112 if (!updatedAddress.isEmpty()) {
1113 updatedAddress +=
", ";
1115 updatedAddress += addressfield;
1118 return updatedAddress;