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 bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
 
  549                     bbartifacts.add(bbart);
 
  554                     for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
 
  555                         BlackboardArtifact downloadSourceArt =  downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
 
  556                         downloadSourceArt.addAttributes(createDownloadSourceAttributes(source));
 
  557                         bbartifacts.add(downloadSourceArt);
 
  560                 } 
catch (TskCoreException ex) {
 
  561                     logger.log(Level.SEVERE, String.format(
"Error creating download source artifact for file  '%s'",
 
  562                         downloadedFilePath), ex); 
 
  566                 this.addErrorMessage(
 
  567                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getDlPre24.errMsg.errParsingArtifacts",
 
  568                                 this.getName(), errors));
 
  575         postArtifacts(bbartifacts);
 
  583     private void getDownloadVersion24() {
 
  584         FileManager fileManager = currentCase.getServices().getFileManager();
 
  585         List<AbstractFile> downloadsFiles;
 
  587             downloadsFiles = fileManager.findFiles(dataSource, 
"places.sqlite", 
"Firefox"); 
 
  588         } 
catch (TskCoreException ex) {
 
  589             String msg = NbBundle.getMessage(this.getClass(), 
"Firefox.getDlV24.errMsg.errFetchFiles");
 
  590             logger.log(Level.WARNING, msg);
 
  591             this.addErrorMessage(this.getName() + 
": " + msg);
 
  595         if (downloadsFiles.isEmpty()) {
 
  596             logger.log(Level.INFO, 
"Didn't find any version-24.0 Firefox download files."); 
 
  601         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  603         for (AbstractFile downloadsFile : downloadsFiles) {
 
  604             if (downloadsFile.getSize() == 0) {
 
  607             String fileName = downloadsFile.getName();
 
  608             String temps = RAImageIngestModule.getRATempPath(currentCase, 
"firefox") + File.separator + fileName + 
"-downloads" + j + 
".db"; 
 
  611                 ContentUtils.writeToFile(downloadsFile, 
new File(temps), context::dataSourceIngestIsCancelled);
 
  612             } 
catch (ReadContentInputStreamException ex) {
 
  613                 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
 
  614                         fileName, downloadsFile.getId()), ex); 
 
  615                 this.addErrorMessage(
 
  616                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
 
  619             } 
catch (IOException ex) {
 
  620                 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
 
  621                         temps, fileName, downloadsFile.getId()), ex); 
 
  622                 this.addErrorMessage(
 
  623                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getDlV24.errMsg.errAnalyzeFile", this.getName(),
 
  627             File dbFile = 
new File(temps);
 
  628             if (context.dataSourceIngestIsCancelled()) {
 
  633             List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V24);
 
  635             logger.log(Level.INFO, 
"{0} - Now getting downloads from {1} with {2} artifacts identified.", 
new Object[]{moduleName, temps, tempList.size()}); 
 
  636             for (HashMap<String, Object> result : tempList) {
 
  638                 if (context.dataSourceIngestIsCancelled()) {
 
  642                 String url = result.get(
"url").toString();
 
  644                 Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  646                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
 
  647                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  653                 String target = result.get(
"target").toString(); 
 
  654                 String downloadedFilePath = 
"";
 
  655                 if (target != null) {
 
  657                         downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///", 
""), 
"UTF-8"); 
 
  658                         bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
 
  659                                 RecentActivityExtracterModuleFactory.getModuleName(),
 
  660                                 downloadedFilePath));
 
  661                         long pathID = Util.findID(dataSource, downloadedFilePath);
 
  663                             bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
 
  664                                     RecentActivityExtracterModuleFactory.getModuleName(),
 
  667                     } 
catch (UnsupportedEncodingException ex) {
 
  668                         logger.log(Level.SEVERE, 
"Error decoding Firefox download URL in " + temps, ex); 
 
  672                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
 
  673                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  674                         Long.valueOf(result.get(
"lastModified").toString()))); 
 
  675                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
 
  676                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  677                         NbBundle.getMessage(this.getClass(), 
"Firefox.moduleName")));
 
  678                 String domain = extractDomain(url);
 
  679                 if (domain != null && domain.isEmpty() == 
false) {
 
  680                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
 
  681                         RecentActivityExtracterModuleFactory.getModuleName(), domain)); 
 
  684                 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
 
  686                     bbartifacts.add(bbart);
 
  691                     for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
 
  692                         BlackboardArtifact downloadSourceArt =  downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
 
  693                         downloadSourceArt.addAttributes(createDownloadSourceAttributes(url));
 
  694                         bbartifacts.add(downloadSourceArt);
 
  697                 } 
catch (TskCoreException ex) {
 
  698                     logger.log(Level.SEVERE, String.format(
"Error creating download source artifact for file  '%s'",
 
  699                         downloadedFilePath), ex); 
 
  703                 this.addErrorMessage(NbBundle.getMessage(
this.getClass(), 
"Firefox.getDlV24.errMsg.errParsingArtifacts",
 
  704                         this.getName(), errors));
 
  711         postArtifacts(bbartifacts);
 
  718     private void getFormsHistory() {
 
  719         FileManager fileManager = currentCase.getServices().getFileManager();
 
  720         List<AbstractFile> formHistoryFiles;
 
  723         Set<String> excludedFieldNames = 
new HashSet<>(Arrays.asList(
 
  729             formHistoryFiles = fileManager.findFiles(dataSource, 
"formhistory.sqlite", 
"Firefox"); 
 
  730         } 
catch (TskCoreException ex) {
 
  731             String msg = NbBundle.getMessage(this.getClass(), 
"Firefox.getFormsAutofill.errMsg.errFetchingFiles");
 
  732             logger.log(Level.WARNING, msg);
 
  733             this.addErrorMessage(this.getName() + 
": " + msg);
 
  737         if (formHistoryFiles.isEmpty()) {
 
  738             String msg = NbBundle.getMessage(this.getClass(), 
"Firefox.getFormsAutofill.errMsg.noFilesFound");
 
  739             logger.log(Level.INFO, msg);
 
  744         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  746         for (AbstractFile formHistoryFile : formHistoryFiles) {
 
  747             if (formHistoryFile.getSize() == 0) {
 
  751             String fileName = formHistoryFile.getName();
 
  752             String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, 
"firefox") + File.separator + fileName + j + 
".db"; 
 
  754                 ContentUtils.writeToFile(formHistoryFile, 
new File(tempFilePath), context::dataSourceIngestIsCancelled);
 
  755             } 
catch (ReadContentInputStreamException ex) {
 
  756                 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
 
  757                         fileName, formHistoryFile.getId()), ex); 
 
  758                 this.addErrorMessage(
 
  759                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
 
  762             } 
catch (IOException ex) {
 
  763                 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
 
  764                         tempFilePath, fileName, formHistoryFile.getId()), ex); 
 
  765                 this.addErrorMessage(
 
  766                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
 
  770             File dbFile = 
new File(tempFilePath);
 
  771             if (context.dataSourceIngestIsCancelled()) {
 
  777             boolean isFirefoxV64 = Util.checkColumn(
"timesUsed", 
"moz_formhistory", tempFilePath);
 
  778             String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
 
  780             List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
 
  781             logger.log(Level.INFO, 
"{0} - Now getting history from {1} with {2} artifacts identified.", 
new Object[]{moduleName, tempFilePath, tempList.size()}); 
 
  782             for (HashMap<String, Object> result : tempList) {
 
  784                 if (context.dataSourceIngestIsCancelled()) {
 
  788                 Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  790                 String fieldName = ((result.get(
"fieldname").toString() != null) ? result.get(
"fieldname").toString() : 
"");
 
  792                 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
 
  796                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
 
  797                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  800                 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
 
  801                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  802                         ((result.get(
"value").toString() != null) ? result.get(
"value").toString() : 
""))); 
 
  806                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
 
  807                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  808                         (Long.valueOf(result.get(
"firstUsed").toString()) / 1000000))); 
 
  810                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
 
  811                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  812                         (Long.valueOf(result.get(
"lastUsed").toString()) / 1000000))); 
 
  814                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
 
  815                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  816                         (Integer.valueOf(result.get(
"timesUsed").toString())))); 
 
  820                 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes);
 
  822                     bbartifacts.add(bbart);
 
  829         postArtifacts(bbartifacts);
 
  838     private void getAutofillProfiles() {
 
  839         FileManager fileManager = currentCase.getServices().getFileManager();
 
  840         List<AbstractFile> autofillProfilesFiles;
 
  842             autofillProfilesFiles = fileManager.findFiles(dataSource, 
"autofill-profiles.json", 
"Firefox"); 
 
  843         } 
catch (TskCoreException ex) {
 
  844             String msg = NbBundle.getMessage(this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errGettingFiles");
 
  845             logger.log(Level.SEVERE, msg, ex);
 
  846             this.addErrorMessage(this.getName() + 
": " + msg);
 
  850         if (autofillProfilesFiles.isEmpty()) {
 
  851             logger.log(Level.INFO, 
"Didn't find any Firefox Autofill Profiles files."); 
 
  856         Collection<BlackboardArtifact> bbartifacts = 
new ArrayList<>();
 
  859         while (j < autofillProfilesFiles.size()) {
 
  860             AbstractFile profileFile = autofillProfilesFiles.get(j++);
 
  861             if (profileFile.getSize() == 0) {
 
  864             String temps = RAImageIngestModule.getRATempPath(currentCase, 
"Firefox") + File.separator + profileFile.getName() + j + 
".json"; 
 
  866                 ContentUtils.writeToFile(profileFile, 
new File(temps), context::dataSourceIngestIsCancelled);
 
  867             } 
catch (ReadContentInputStreamException ex) {
 
  868                 logger.log(Level.WARNING, String.format(
"Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
 
  869                         profileFile.getName(), profileFile.getId()), ex); 
 
  870                 this.addErrorMessage(NbBundle.getMessage(
this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
 
  871                         this.getName(), profileFile.getName()));
 
  873             } 
catch (IOException ex) {
 
  874                 logger.log(Level.SEVERE, String.format(
"Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
 
  875                         temps, profileFile.getName(), profileFile.getId()), ex); 
 
  876                 this.addErrorMessage(NbBundle.getMessage(
this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
 
  877                         this.getName(), profileFile.getName()));
 
  881             logger.log(Level.INFO, 
"{0}- Now getting Bookmarks from {1}", 
new Object[]{moduleName, temps}); 
 
  882             File dbFile = 
new File(temps);
 
  883             if (context.dataSourceIngestIsCancelled()) {
 
  888             FileReader tempReader;
 
  890                 tempReader = 
new FileReader(temps);
 
  891             } 
catch (FileNotFoundException ex) {
 
  892                 logger.log(Level.SEVERE, 
"Error while trying to read the Autofill profiles json file for Firefox.", ex); 
 
  893                 this.addErrorMessage(
 
  894                         NbBundle.getMessage(
this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
 
  895                                 profileFile.getName()));
 
  899             final JsonParser parser = 
new JsonParser();
 
  901             JsonObject jsonRootObject;
 
  902             JsonArray jAddressesArray;
 
  905                 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
 
  906                 jAddressesArray = jsonRootObject.getAsJsonArray(
"addresses"); 
 
  907             } 
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
 
  908                 logger.log(Level.WARNING, 
"Error parsing Json for Firefox Autofill profiles.", ex); 
 
  909                 this.addErrorMessage(NbBundle.getMessage(
this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
 
  910                         this.getName(), profileFile.getName()));
 
  914             for (JsonElement result : jAddressesArray) {
 
  915                 JsonObject address = result.getAsJsonObject();
 
  916                 if (address == null) {
 
  920                 JsonElement nameEl = address.get(
"name"); 
 
  921                 String name = (nameEl != null) ? nameEl.getAsString() : 
"";
 
  923                 JsonElement emailEl = address.get(
"email"); 
 
  924                 String email = (emailEl != null) ? emailEl.getAsString() : 
"";
 
  926                 JsonElement telEl = address.get(
"tel"); 
 
  927                 String tel = (telEl != null) ? telEl.getAsString() : 
"";
 
  928                 JsonElement telCountryCodeEl = address.get(
"tel-country-code"); 
 
  929                 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() : 
"";
 
  930                 JsonElement telNationalEl = address.get(
"tel-national"); 
 
  931                 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() : 
"";
 
  933                 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
 
  935                 JsonElement createdEl = address.get(
"timeCreated"); 
 
  936                 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);     
 
  937                 JsonElement lastusedEl = address.get(
"timeLastUsed"); 
 
  938                 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0); 
 
  939                 JsonElement timesUsedEl = address.get(
"timesUsed"); 
 
  940                 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0); 
 
  942                 JsonElement addressLine1El = address.get(
"address-line1"); 
 
  943                 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() : 
"";
 
  944                 JsonElement addressLine2El = address.get(
"address-line2"); 
 
  945                 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() : 
"";
 
  946                 JsonElement addressLine3El = address.get(
"address-line3"); 
 
  947                 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() : 
"";
 
  949                 JsonElement postalCodeEl = address.get(
"postal-code"); 
 
  950                 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() : 
"";
 
  951                 JsonElement countryEl = address.get(
"country"); 
 
  952                 String country = (countryEl != null) ? countryEl.getAsString() : 
"";
 
  954                 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
 
  957                     Collection<BlackboardAttribute> bbattributes = 
new ArrayList<>();
 
  958                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME_PERSON,
 
  959                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  962                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
 
  963                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  966                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
 
  967                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  970                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCATION,
 
  971                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  974                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
 
  975                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  978                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
 
  979                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  982                     bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
 
  983                         RecentActivityExtracterModuleFactory.getModuleName(),
 
  986                     BlackboardArtifact bbart = profileFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS);  
 
  989                         bbart.addAttributes(bbattributes);
 
  990                         bbartifacts.add(bbart);
 
  994                     if (email != null && !email.isEmpty()) {
 
  996                             Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,  NbBundle.getMessage(
this.getClass(), 
"Firefox.parentModuleName"), profileFile);
 
  997                         } 
catch (NoCurrentCaseException | TskCoreException ex) {
 
  998                             logger.log(Level.SEVERE, String.format(
"Error creating email account instance for '%s' from Firefox profiles file '%s' .",
 
  999                                 email, profileFile.getName()), ex); 
 
 1004                     if (phoneNumber != null && !phoneNumber.isEmpty()) {
 
 1006                             Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, phoneNumber,  NbBundle.getMessage(
this.getClass(), 
"Firefox.parentModuleName"), profileFile);
 
 1007                         } 
catch (NoCurrentCaseException | TskCoreException ex) {
 
 1008                             logger.log(Level.SEVERE, String.format(
"Error creating phone number account instance for '%s' from Chrome profiles file '%s' .",
 
 1009                                 phoneNumber, profileFile.getName()), ex); 
 
 1013                 } 
catch (TskCoreException ex) {
 
 1014                     logger.log(Level.SEVERE, 
"Error while trying to insert Firefox Autofill profile artifact{0}", ex); 
 
 1015                     this.addErrorMessage(
 
 1016                             NbBundle.getMessage(
this.getClass(), 
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
 
 1017                                     this.getName(), profileFile.getName()));
 
 1023         postArtifacts(bbartifacts);
 
 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;