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;