1 /*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 2012-2020 Basis Technology Corp.
6 *
7 * Copyright 2012 42six Solutions.
8 * Contact: aebadirad <at> 42six <dot> com
9 * Project Contact/Architect: carrier <at> sleuthkit <dot> org
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23 package org.sleuthkit.autopsy.recentactivity;
24
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;
31 import java.io.File;
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;
43 import java.util.Set;
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;
66
67 @Messages({
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"
74 })
75
79 class Firefox extends Extract {
80
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 "
87 + "AND hidden = 0"; //NON-NLS
88 private static final String COOKIE_QUERY = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies"; //NON-NLS
89 private static final String COOKIE_QUERY_V3 = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies"; //NON-NLS
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"; //NON-NLS
91 private static final String DOWNLOAD_QUERY = "SELECT target, source,(startTime/1000000) AS startTime, maxBytes FROM moz_downloads"; //NON-NLS
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'"; //NON-NLS
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;
101
102 Firefox() {
103 moduleName = NbBundle.getMessage(Firefox.class, "Firefox.moduleName");
104 }
105
106 @Override
107 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
108 this.dataSource = dataSource;
109 this.context = context;
110 dataFound = false;
111
112 progressBar.progress(Bundle.Progress_Message_Firefox_History());
113 this.getHistory();
114
115 progressBar.progress(Bundle.Progress_Message_Firefox_Bookmarks());
116 this.getBookmark();
117
118 progressBar.progress(Bundle.Progress_Message_Firefox_Downloads());
119 this.getDownload();
120
121 progressBar.progress(Bundle.Progress_Message_Firefox_Cookies());
122 this.getCookie();
123
124 progressBar.progress(Bundle.Progress_Message_Firefox_FormHistory());
125 this.getFormsHistory();
126
127 progressBar.progress(Bundle.Progress_Message_Firefox_AutoFill());
128 this.getAutofillProfiles();
129 }
130
131 private void getHistory() {
132 FileManager fileManager = currentCase.getServices().getFileManager();
133 List<AbstractFile> historyFiles;
134 try {
135 historyFiles = fileManager.findFiles(dataSource, "places.sqlite", "Firefox"); //NON-NLS
136 } catch (TskCoreException ex) {
137 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errFetchingFiles");
138 logger.log(Level.WARNING, msg);
139 this.addErrorMessage(this.getName() + ": " + msg);
140 return;
141 }
142
143 if (historyFiles.isEmpty()) {
144 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.noFilesFound");
145 logger.log(Level.INFO, msg);
146 return;
147 }
148
149 dataFound = true;
150 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
151 int j = 0;
152 for (AbstractFile historyFile : historyFiles) {
153
154 if (context.dataSourceIngestIsCancelled()) {
155 return;
156 }
157
158 if (historyFile.getSize() == 0) {
159 continue;
160 }
161
162 String fileName = historyFile.getName();
163 String temps = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
164 try {
165 ContentUtils.writeToFile(historyFile, new File(temps), context::dataSourceIngestIsCancelled);
166 } catch (ReadContentInputStreamException ex) {
167 logger.log(Level.WARNING, String.format("Error reading Firefox web history artifacts file '%s' (id=%d).",
168 fileName, historyFile.getId()), ex); //NON-NLS
169 this.addErrorMessage(
170 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
171 fileName));
172 continue;
173 } catch (IOException ex) {
174 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
175 temps, fileName, historyFile.getId()), ex); //NON-NLS
176 this.addErrorMessage(
177 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
178 fileName));
179 continue;
180 }
181 File dbFile = new File(temps);
182 if (context.dataSourceIngestIsCancelled()) {
183 dbFile.delete();
184 break;
185 }
186 List<HashMap<String, Object>> tempList = this.dbConnect(temps, HISTORY_QUERY);
187 logger.log(Level.INFO, "{0} - Now getting history from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
188 for (HashMap<String, Object> result : tempList) {
189
190 if (context.dataSourceIngestIsCancelled()) {
191 return;
192 }
193
194 String url = result.get("url").toString();
195
196 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
197 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
198 RecentActivityExtracterModuleFactory.getModuleName(),
199 ((url != null) ? url : ""))); //NON-NLS
200 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
201 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
202 RecentActivityExtracterModuleFactory.getModuleName(),
203 (Long.valueOf(result.get("visit_date").toString())))); //NON-NLS
204 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
205 RecentActivityExtracterModuleFactory.getModuleName(),
206 ((result.get("ref").toString() != null) ? result.get("ref").toString() : ""))); //NON-NLS
207 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
208 RecentActivityExtracterModuleFactory.getModuleName(),
209 ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
210 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
211 RecentActivityExtracterModuleFactory.getModuleName(),
212 NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
213 String domain = extractDomain(url);
214 if (domain != null && domain.isEmpty() == false) {
215 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
216 RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS
217
218 }
219 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
220 if (bbart != null) {
221 bbartifacts.add(bbart);
222 }
223 }
224 ++j;
225 dbFile.delete();
226 }
227
228 postArtifacts(bbartifacts);
229 }
230
234 private void getBookmark() {
235
236 FileManager fileManager = currentCase.getServices().getFileManager();
237 List<AbstractFile> bookmarkFiles;
238 try {
239 bookmarkFiles = fileManager.findFiles(dataSource, "places.sqlite", "Firefox"); //NON-NLS
240 } catch (TskCoreException ex) {
241 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getBookmark.errMsg.errFetchFiles");
242 logger.log(Level.WARNING, msg);
243 this.addErrorMessage(this.getName() + ": " + msg);
244 return;
245 }
246
247 if (bookmarkFiles.isEmpty()) {
248 logger.log(Level.INFO, "Didn't find any firefox bookmark files."); //NON-NLS
249 return;
250 }
251
252 dataFound = true;
253 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
254 int j = 0;
255 for (AbstractFile bookmarkFile : bookmarkFiles) {
256 if (bookmarkFile.getSize() == 0) {
257 continue;
258 }
259 String fileName = bookmarkFile.getName();
260 String temps = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
261 try {
262 ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
263 } catch (ReadContentInputStreamException ex) {
264 logger.log(Level.WARNING, String.format("Error reading Firefox bookmark artifacts file '%s' (id=%d).",
265 fileName, bookmarkFile.getId()), ex); //NON-NLS
266 this.addErrorMessage(
267 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
268 fileName));
269 continue;
270 } catch (IOException ex) {
271 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox bookmark artifacts file '%s' (id=%d).",
272 temps, fileName, bookmarkFile.getId()), ex); //NON-NLS
273 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getBookmark.errMsg.errAnalyzeFile",
274 this.getName(), fileName));
275 continue;
276 }
277 File dbFile = new File(temps);
278 if (context.dataSourceIngestIsCancelled()) {
279 dbFile.delete();
280 break;
281 }
282 List<HashMap<String, Object>> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
283 logger.log(Level.INFO, "{0} - Now getting bookmarks from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
284 for (HashMap<String, Object> result : tempList) {
285
286 if (context.dataSourceIngestIsCancelled()) {
287 break;
288 }
289
290 String url = result.get("url").toString();
291
292 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
293 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
294 RecentActivityExtracterModuleFactory.getModuleName(),
295 ((url != null) ? url : ""))); //NON-NLS
296 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
297 RecentActivityExtracterModuleFactory.getModuleName(),
298 ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
299 if (Long.valueOf(result.get("dateAdded").toString()) > 0) { //NON-NLS
300 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
301 RecentActivityExtracterModuleFactory.getModuleName(),
302 (Long.valueOf(result.get("dateAdded").toString())))); //NON-NLS
303 }
304 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
305 RecentActivityExtracterModuleFactory.getModuleName(),
306 NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
307 String domain = extractDomain(url);
308 if (domain != null && domain.isEmpty() == false) {
309 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
310 RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS
311 }
312
313 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes);
314 if (bbart != null) {
315 bbartifacts.add(bbart);
316 }
317 }
318 ++j;
319 dbFile.delete();
320 }
321
322 postArtifacts(bbartifacts);
323 }
324
328 private void getCookie() {
329 FileManager fileManager = currentCase.getServices().getFileManager();
330 List<AbstractFile> cookiesFiles;
331 try {
332 cookiesFiles = fileManager.findFiles(dataSource, "cookies.sqlite", "Firefox"); //NON-NLS
333 } catch (TskCoreException ex) {
334 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getCookie.errMsg.errFetchFile");
335 logger.log(Level.WARNING, msg);
336 this.addErrorMessage(this.getName() + ": " + msg);
337 return;
338 }
339
340 if (cookiesFiles.isEmpty()) {
341 logger.log(Level.INFO, "Didn't find any Firefox cookie files."); //NON-NLS
342 return;
343 }
344
345 dataFound = true;
346 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
347 int j = 0;
348 for (AbstractFile cookiesFile : cookiesFiles) {
349 if (cookiesFile.getSize() == 0) {
350 continue;
351 }
352 String fileName = cookiesFile.getName();
353 String temps = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
354 try {
355 ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
356 } catch (ReadContentInputStreamException ex) {
357 logger.log(Level.WARNING, String.format("Error reading Firefox cookie artifacts file '%s' (id=%d).",
358 fileName, cookiesFile.getId()), ex); //NON-NLS
359 this.addErrorMessage(
360 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
361 fileName));
362 continue;
363 } catch (IOException ex) {
364 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox cookie artifacts file '%s' (id=%d).",
365 temps, fileName, cookiesFile.getId()), ex); //NON-NLS
366 this.addErrorMessage(
367 NbBundle.getMessage(this.getClass(), "Firefox.getCookie.errMsg.errAnalyzeFile", this.getName(),
368 fileName));
369 continue;
370 }
371 File dbFile = new File(temps);
372 if (context.dataSourceIngestIsCancelled()) {
373 dbFile.delete();
374 break;
375 }
376 boolean checkColumn = Util.checkColumn("creationTime", "moz_cookies", temps); //NON-NLS
377 String query;
378 if (checkColumn) {
379 query = COOKIE_QUERY;
380 } else {
381 query = COOKIE_QUERY_V3;
382 }
383
384 List<HashMap<String, Object>> tempList = this.dbConnect(temps, query);
385 logger.log(Level.INFO, "{0} - Now getting cookies from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
386 for (HashMap<String, Object> result : tempList) {
387
388 if (context.dataSourceIngestIsCancelled()) {
389 break;
390 }
391
392 String host = result.get("host").toString();
393
394 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
395 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
396 RecentActivityExtracterModuleFactory.getModuleName(),
397 ((host != null) ? host : ""))); //NON-NLS
398 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
399 RecentActivityExtracterModuleFactory.getModuleName(),
400 (Long.valueOf(result.get("lastAccessed").toString())))); //NON-NLS
401 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
402 RecentActivityExtracterModuleFactory.getModuleName(),
403 ((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //NON-NLS
404 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
405 RecentActivityExtracterModuleFactory.getModuleName(),
406 ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
407 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
408 RecentActivityExtracterModuleFactory.getModuleName(),
409 NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
410
411 if (checkColumn == true) {
412 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
413 RecentActivityExtracterModuleFactory.getModuleName(),
414 (Long.valueOf(result.get("creationTime").toString())))); //NON-NLS
415 }
416 String domain = extractDomain(host);
417 if (domain != null && domain.isEmpty() == false) {
418 domain = domain.replaceFirst("^\\.+(?!$)", "");
419 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
420 RecentActivityExtracterModuleFactory.getModuleName(), domain));
421 }
422
423 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
424 if (bbart != null) {
425 bbartifacts.add(bbart);
426 }
427 }
428 ++j;
429 dbFile.delete();
430 }
431
432 postArtifacts(bbartifacts);
433 }
434
438 private void getDownload() {
439 getDownloadPreVersion24();
440 getDownloadVersion24();
441 }
442
448 private void getDownloadPreVersion24() {
449
450 FileManager fileManager = currentCase.getServices().getFileManager();
451 List<AbstractFile> downloadsFiles;
452 try {
453 downloadsFiles = fileManager.findFiles(dataSource, "downloads.sqlite", "Firefox"); //NON-NLS
454 } catch (TskCoreException ex) {
455 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getDlPre24.errMsg.errFetchFiles");
456 logger.log(Level.WARNING, msg);
457 this.addErrorMessage(this.getName() + ": " + msg);
458 return;
459 }
460
461 if (downloadsFiles.isEmpty()) {
462 logger.log(Level.INFO, "Didn't find any pre-version-24.0 Firefox download files."); //NON-NLS
463 return;
464 }
465
466 dataFound = true;
467 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
468 int j = 0;
469 for (AbstractFile downloadsFile : downloadsFiles) {
470 if (downloadsFile.getSize() == 0) {
471 continue;
472 }
473 String fileName = downloadsFile.getName();
474 String temps = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
475 int errors = 0;
476 try {
477 ContentUtils.writeToFile(downloadsFile, new File(temps), context::dataSourceIngestIsCancelled);
478 } catch (ReadContentInputStreamException ex) {
479 logger.log(Level.WARNING, String.format("Error reading Firefox download artifacts file '%s' (id=%d).",
480 fileName, downloadsFile.getId()), ex); //NON-NLS
481 this.addErrorMessage(
482 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
483 fileName));
484 continue;
485 } catch (IOException ex) {
486 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
487 temps, fileName, downloadsFile.getId()), ex); //NON-NLS
488 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getDlPre24.errMsg.errAnalyzeFiles",
489 this.getName(), fileName));
490 continue;
491 }
492 File dbFile = new File(temps);
493 if (context.dataSourceIngestIsCancelled()) {
494 dbFile.delete();
495 break;
496 }
497
498 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
499 logger.log(Level.INFO, "{0}- Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
500 for (HashMap<String, Object> result : tempList) {
501
502 if (context.dataSourceIngestIsCancelled()) {
503 break;
504 }
505
506 String source = result.get("source").toString();
507
508 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
509
510 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
511 RecentActivityExtracterModuleFactory.getModuleName(),
512 source)); //NON-NLS
513 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
514 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
515 RecentActivityExtracterModuleFactory.getModuleName(),
516 (Long.valueOf(result.get("startTime").toString())))); //NON-NLS
517
518 String target = result.get("target").toString(); //NON-NLS
519 String downloadedFilePath = "";
520 if (target != null) {
521 try {
522 downloadedFilePath = URLDecoder.decode(target.replaceAll("file:///", ""), "UTF-8"); //NON-NLS
523 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
524 RecentActivityExtracterModuleFactory.getModuleName(),
525 downloadedFilePath));
526 long pathID = Util.findID(dataSource, downloadedFilePath);
527 if (pathID != -1) {
528 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
529 RecentActivityExtracterModuleFactory.getModuleName(),
530 pathID));
531 }
532 } catch (UnsupportedEncodingException ex) {
533 logger.log(Level.SEVERE, "Error decoding Firefox download URL in " + temps, ex); //NON-NLS
534 errors++;
535 }
536 }
537
538 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
539 RecentActivityExtracterModuleFactory.getModuleName(),
540 NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
541 String domain = extractDomain(source);
542 if (domain != null && domain.isEmpty() == false) {
543 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
544 RecentActivityExtracterModuleFactory.getModuleName(),
545 domain)); //NON-NLS
546 }
547
548 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
549 if (webDownloadArtifact != null) {
550 bbartifacts.add(webDownloadArtifact);
551
552 // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
553 try {
554 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
555 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
556 associatedObjectArtifact.addAttribute(
557 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
558 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
559
560 bbartifacts.add(associatedObjectArtifact);
561 break;
562 }
563 } catch (TskCoreException ex) {
564 logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'",
565 downloadedFilePath), ex); //NON-NLS
566 }
567 }
568 }
569 if (errors > 0) {
570 this.addErrorMessage(
571 NbBundle.getMessage(this.getClass(), "Firefox.getDlPre24.errMsg.errParsingArtifacts",
572 this.getName(), errors));
573 }
574 j++;
575 dbFile.delete();
576 break;
577 }
578
579 postArtifacts(bbartifacts);
580 }
581
587 private void getDownloadVersion24() {
588 FileManager fileManager = currentCase.getServices().getFileManager();
589 List<AbstractFile> downloadsFiles;
590 try {
591 downloadsFiles = fileManager.findFiles(dataSource, "places.sqlite", "Firefox"); //NON-NLS
592 } catch (TskCoreException ex) {
593 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errFetchFiles");
594 logger.log(Level.WARNING, msg);
595 this.addErrorMessage(this.getName() + ": " + msg);
596 return;
597 }
598
599 if (downloadsFiles.isEmpty()) {
600 logger.log(Level.INFO, "Didn't find any version-24.0 Firefox download files."); //NON-NLS
601 return;
602 }
603
604 dataFound = true;
605 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
606 int j = 0;
607 for (AbstractFile downloadsFile : downloadsFiles) {
608 if (downloadsFile.getSize() == 0) {
609 continue;
610 }
611 String fileName = downloadsFile.getName();
612 String temps = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + "-downloads" + j + ".db"; //NON-NLS
613 int errors = 0;
614 try {
615 ContentUtils.writeToFile(downloadsFile, new File(temps), context::dataSourceIngestIsCancelled);
616 } catch (ReadContentInputStreamException ex) {
617 logger.log(Level.WARNING, String.format("Error reading Firefox download artifacts file '%s' (id=%d).",
618 fileName, downloadsFile.getId()), ex); //NON-NLS
619 this.addErrorMessage(
620 NbBundle.getMessage(this.getClass(), "Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
621 fileName));
622 continue;
623 } catch (IOException ex) {
624 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
625 temps, fileName, downloadsFile.getId()), ex); //NON-NLS
626 this.addErrorMessage(
627 NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errAnalyzeFile", this.getName(),
628 fileName));
629 continue;
630 }
631 File dbFile = new File(temps);
632 if (context.dataSourceIngestIsCancelled()) {
633 dbFile.delete();
634 break;
635 }
636
637 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V24);
638
639 logger.log(Level.INFO, "{0} - Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
640 for (HashMap<String, Object> result : tempList) {
641
642 if (context.dataSourceIngestIsCancelled()) {
643 break;
644 }
645
646 String url = result.get("url").toString();
647
648 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
649
650 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
651 RecentActivityExtracterModuleFactory.getModuleName(),
652 url)); //NON-NLS
653 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
654 //TODO Revisit usage of deprecated constructor as per TSK-583
655 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", "Last Visited", (Long.valueOf(result.get("startTime").toString()))));
656
657 String target = result.get("target").toString(); //NON-NLS
658 String downloadedFilePath = "";
659 if (target != null) {
660 try {
661 downloadedFilePath = URLDecoder.decode(target.replaceAll("file:///", ""), "UTF-8"); //NON-NLS
662 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
663 RecentActivityExtracterModuleFactory.getModuleName(),
664 downloadedFilePath));
665 long pathID = Util.findID(dataSource, downloadedFilePath);
666 if (pathID != -1) {
667 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
668 RecentActivityExtracterModuleFactory.getModuleName(),
669 pathID));
670 }
671 } catch (UnsupportedEncodingException ex) {
672 logger.log(Level.SEVERE, "Error decoding Firefox download URL in " + temps, ex); //NON-NLS
673 errors++;
674 }
675 }
676 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
677 RecentActivityExtracterModuleFactory.getModuleName(),
678 Long.valueOf(result.get("lastModified").toString()))); //NON-NLS
679 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
680 RecentActivityExtracterModuleFactory.getModuleName(),
681 NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
682 String domain = extractDomain(url);
683 if (domain != null && domain.isEmpty() == false) {
684 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
685 RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS
686 }
687
688 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
689 if (webDownloadArtifact != null) {
690 bbartifacts.add(webDownloadArtifact);
691
692 // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
693 try {
694 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
695 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
696 associatedObjectArtifact.addAttribute(
697 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
698 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
699 bbartifacts.add(associatedObjectArtifact);
700 break;
701 }
702 } catch (TskCoreException ex) {
703 logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'",
704 downloadedFilePath), ex); //NON-NLS
705 }
706 }
707 }
708 if (errors > 0) {
709 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errParsingArtifacts",
710 this.getName(), errors));
711 }
712 j++;
713 dbFile.delete();
714 break;
715 }
716
717 postArtifacts(bbartifacts);
718 }
719
724 private void getFormsHistory() {
725 FileManager fileManager = currentCase.getServices().getFileManager();
726 List<AbstractFile> formHistoryFiles;
727
728 // Some fields are just noisy and can me excluded
729 Set<String> excludedFieldNames = new HashSet<>(Arrays.asList(
730 "it", // some kind of timestamp
731 "ts" // some kind of timestamp
732 ));
733
734 try {
735 formHistoryFiles = fileManager.findFiles(dataSource, "formhistory.sqlite", "Firefox"); //NON-NLS
736 } catch (TskCoreException ex) {
737 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errFetchingFiles");
738 logger.log(Level.WARNING, msg);
739 this.addErrorMessage(this.getName() + ": " + msg);
740 return;
741 }
742
743 if (formHistoryFiles.isEmpty()) {
744 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.noFilesFound");
745 logger.log(Level.INFO, msg);
746 return;
747 }
748
749 dataFound = true;
750 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
751 int j = 0;
752 for (AbstractFile formHistoryFile : formHistoryFiles) {
753 if (formHistoryFile.getSize() == 0) {
754 continue;
755 }
756
757 String fileName = formHistoryFile.getName();
758 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "firefox") + File.separator + fileName + j + ".db"; //NON-NLS
759 try {
760 ContentUtils.writeToFile(formHistoryFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
761 } catch (ReadContentInputStreamException ex) {
762 logger.log(Level.WARNING, String.format("Error reading Firefox web history artifacts file '%s' (id=%d).",
763 fileName, formHistoryFile.getId()), ex); //NON-NLS
764 this.addErrorMessage(
765 NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
766 fileName));
767 continue;
768 } catch (IOException ex) {
769 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
770 tempFilePath, fileName, formHistoryFile.getId()), ex); //NON-NLS
771 this.addErrorMessage(
772 NbBundle.getMessage(this.getClass(), "Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
773 fileName));
774 continue;
775 }
776 File dbFile = new File(tempFilePath);
777 if (context.dataSourceIngestIsCancelled()) {
778 dbFile.delete();
779 break;
780 }
781
782 // The table schema is a little different in newer version of Firefox
783 boolean isFirefoxV64 = Util.checkColumn("timesUsed", "moz_formhistory", tempFilePath);
784 String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
785
786 List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
787 logger.log(Level.INFO, "{0} - Now getting history from {1} with {2} artifacts identified.", new Object[]{moduleName, tempFilePath, tempList.size()}); //NON-NLS
788 for (HashMap<String, Object> result : tempList) {
789
790 if (context.dataSourceIngestIsCancelled()) {
791 break;
792 }
793
794 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
795
796 String fieldName = ((result.get("fieldname").toString() != null) ? result.get("fieldname").toString() : "");
797 // filter out unuseful values
798 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
799 continue;
800 }
801
802 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
803 RecentActivityExtracterModuleFactory.getModuleName(),
804 fieldName)); //NON-NLS
805
806 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
807 RecentActivityExtracterModuleFactory.getModuleName(),
808 ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
809
810 // Newer versions of firefox have additional columns
811 if (isFirefoxV64) {
812 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
813 RecentActivityExtracterModuleFactory.getModuleName(),
814 (Long.valueOf(result.get("firstUsed").toString()) / 1000000))); //NON-NLS
815
816 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
817 RecentActivityExtracterModuleFactory.getModuleName(),
818 (Long.valueOf(result.get("lastUsed").toString()) / 1000000))); //NON-NLS
819
820 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
821 RecentActivityExtracterModuleFactory.getModuleName(),
822 (Integer.valueOf(result.get("timesUsed").toString())))); //NON-NLS
823
824 }
825 // Add artifact
826 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes);
827 if (bbart != null) {
828 bbartifacts.add(bbart);
829 }
830 }
831 ++j;
832 dbFile.delete();
833 }
834
835 postArtifacts(bbartifacts);
836 }
837
838
844 private void getAutofillProfiles() {
845 FileManager fileManager = currentCase.getServices().getFileManager();
846 List<AbstractFile> autofillProfilesFiles;
847 try {
848 autofillProfilesFiles = fileManager.findFiles(dataSource, "autofill-profiles.json", "Firefox"); //NON-NLS
849 } catch (TskCoreException ex) {
850 String msg = NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errGettingFiles");
851 logger.log(Level.SEVERE, msg, ex);
852 this.addErrorMessage(this.getName() + ": " + msg);
853 return;
854 }
855
856 if (autofillProfilesFiles.isEmpty()) {
857 logger.log(Level.INFO, "Didn't find any Firefox Autofill Profiles files."); //NON-NLS
858 return;
859 }
860
861 dataFound = true;
862 int j = 0;
863
864 while (j < autofillProfilesFiles.size()) {
865 AbstractFile profileFile = autofillProfilesFiles.get(j++);
866 if (profileFile.getSize() == 0) {
867 continue;
868 }
869 String temps = RAImageIngestModule.getRATempPath(currentCase, "Firefox") + File.separator + profileFile.getName() + j + ".json"; //NON-NLS
870 try {
871 ContentUtils.writeToFile(profileFile, new File(temps), context::dataSourceIngestIsCancelled);
872 } catch (ReadContentInputStreamException ex) {
873 logger.log(Level.WARNING, String.format("Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
874 profileFile.getName(), profileFile.getId()), ex); //NON-NLS
875 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
876 this.getName(), profileFile.getName()));
877 continue;
878 } catch (IOException ex) {
879 logger.log(Level.SEVERE, String.format("Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
880 temps, profileFile.getName(), profileFile.getId()), ex); //NON-NLS
881 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
882 this.getName(), profileFile.getName()));
883 continue;
884 }
885
886 logger.log(Level.INFO, "{0}- Now getting Bookmarks from {1}", new Object[]{moduleName, temps}); //NON-NLS
887 File dbFile = new File(temps);
888 if (context.dataSourceIngestIsCancelled()) {
889 dbFile.delete();
890 break;
891 }
892
893 FileReader tempReader;
894 try {
895 tempReader = new FileReader(temps);
896 } catch (FileNotFoundException ex) {
897 logger.log(Level.SEVERE, "Error while trying to read the Autofill profiles json file for Firefox.", ex); //NON-NLS
898 this.addErrorMessage(
899 NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
900 profileFile.getName()));
901 continue;
902 }
903
904 final JsonParser parser = new JsonParser();
905
906 JsonObject jsonRootObject;
907 JsonArray jAddressesArray;
908
909 try {
910 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
911 jAddressesArray = jsonRootObject.getAsJsonArray("addresses"); //NON-NLS
912 } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
913 logger.log(Level.WARNING, "Error parsing Json for Firefox Autofill profiles.", ex); //NON-NLS
914 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
915 this.getName(), profileFile.getName()));
916 continue;
917 }
918
919 WebBrowserArtifactsHelper helper;
920 try {
921 // Helper to create web form address artifacts.
922 helper = new WebBrowserArtifactsHelper(
923 Case.getCurrentCaseThrows().getSleuthkitCase(),
924 NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
925 profileFile
926 );
927 } catch (NoCurrentCaseException ex) {
928 logger.log(Level.SEVERE, "No case open, bailing.", ex); //NON-NLS
929 return;
930 }
931
932 for (JsonElement result : jAddressesArray) {
933 JsonObject address = result.getAsJsonObject();
934 if (address == null) {
935 continue;
936 }
937
938 JsonElement nameEl = address.get("name"); //NON-NLS
939 String name = (nameEl != null) ? nameEl.getAsString() : "";
940
941 JsonElement emailEl = address.get("email"); //NON-NLS
942 String email = (emailEl != null) ? emailEl.getAsString() : "";
943
944 JsonElement telEl = address.get("tel"); //NON-NLS
945 String tel = (telEl != null) ? telEl.getAsString() : "";
946 JsonElement telCountryCodeEl = address.get("tel-country-code"); //NON-NLS
947 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() : "";
948 JsonElement telNationalEl = address.get("tel-national"); //NON-NLS
949 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() : "";
950
951 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
952
953 JsonElement createdEl = address.get("timeCreated"); //NON-NLS
954 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);
955 JsonElement lastusedEl = address.get("timeLastUsed"); //NON-NLS
956 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0);
957 JsonElement timesUsedEl = address.get("timesUsed"); //NON-NLS
958 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
959
960 JsonElement addressLine1El = address.get("address-line1"); //NON-NLS
961 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() : "";
962 JsonElement addressLine2El = address.get("address-line2"); //NON-NLS
963 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() : "";
964 JsonElement addressLine3El = address.get("address-line3"); //NON-NLS
965 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() : "";
966
967 JsonElement postalCodeEl = address.get("postal-code"); //NON-NLS
968 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() : "";
969 JsonElement countryEl = address.get("country"); //NON-NLS
970 String country = (countryEl != null) ? countryEl.getAsString() : "";
971
972 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
973
974 try {
975 helper.addWebFormAddress(name, email, phoneNumber,
976 mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
977 } catch (TskCoreException | Blackboard.BlackboardException ex) {
978 logger.log(Level.SEVERE, "Error while trying to insert Firefox Autofill profile artifact{0}", ex); //NON-NLS
979 this.addErrorMessage(
980 NbBundle.getMessage(this.getClass(), "Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
981 this.getName(), profileFile.getName()));
982 }
983 }
984 dbFile.delete();
985 }
986 }
987
996 private String extractDomain(String url) {
997 if (url == null || url.isEmpty()) {
998 return url;
999 }
1000
1001 if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
1002 /*
1003 * Ignore URLs that begin with the matched text.
1004 */
1005 return null;
1006 }
1007
1008 return NetworkUtils.extractDomain(url);
1009 }
1010
1011
1021 private String makeTelNumber(String tel, String telCountryCode, String telNational) {
1022
1023 if (tel != null && !tel.isEmpty()) {
1024 return tel;
1025 }
1026
1027 if ((telCountryCode != null && !telCountryCode.isEmpty()) &&
1028 (telNational != null && !telNational.isEmpty())) {
1029 return telCountryCode + telNational;
1030 }
1031
1032 return "";
1033 }
1034
1046 private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country ) {
1047 String fullAddress = "";
1048 fullAddress = appendAddressField(fullAddress, addressLine1 );
1049 fullAddress = appendAddressField(fullAddress, addressLine2 );
1050 fullAddress = appendAddressField(fullAddress, addressLine3 );
1051 fullAddress = appendAddressField(fullAddress, postalCode );
1052 fullAddress = appendAddressField(fullAddress, country );
1053
1054 return fullAddress;
1055 }
1056
1065 private String appendAddressField(String address, String addressfield) {
1066
1067 String updatedAddress = address;
1068 if (addressfield != null && !addressfield.isEmpty()) {
1069 if (!updatedAddress.isEmpty()) {
1070 updatedAddress += ", ";
1071 }
1072 updatedAddress += addressfield;
1073 }
1074
1075 return updatedAddress;
1076 }
1077
1078 }