Autopsy  4.16.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Chromium.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2012-2020 Basis Technology Corp.
6  *
7  * Copyright 2012 42six Solutions.
8  *
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.common.collect.ImmutableMap;
26 import com.google.gson.JsonArray;
27 import com.google.gson.JsonElement;
28 import com.google.gson.JsonIOException;
29 import com.google.gson.JsonObject;
30 import com.google.gson.JsonParser;
31 import com.google.gson.JsonSyntaxException;
32 import org.openide.util.NbBundle;
34 import java.util.logging.Level;
35 import java.util.*;
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 import java.io.FileReader;
39 import java.io.IOException;
40 import org.apache.commons.io.FilenameUtils;
41 import org.openide.util.NbBundle.Messages;
49 import org.sleuthkit.datamodel.AbstractFile;
50 import org.sleuthkit.datamodel.Blackboard;
51 import org.sleuthkit.datamodel.BlackboardArtifact;
52 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
54 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
55 import org.sleuthkit.datamodel.Content;
56 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
57 import org.sleuthkit.datamodel.TskCoreException;
58 import org.sleuthkit.datamodel.TskData;
59 import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
60 
64 class Chromium extends Extract {
65 
66  private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS
67  + "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS
68  private static final String COOKIE_QUERY = "SELECT name, value, host_key, expires_utc,last_access_utc, creation_utc FROM cookies"; //NON-NLS
69  private static final String DOWNLOAD_QUERY = "SELECT full_path, url, start_time, received_bytes FROM downloads"; //NON-NLS
70  private static final String DOWNLOAD_QUERY_V30 = "SELECT current_path AS full_path, url, start_time, received_bytes FROM downloads, downloads_url_chains WHERE downloads.id=downloads_url_chains.id"; //NON-NLS
71  private static final String LOGIN_QUERY = "SELECT origin_url, username_value, date_created, signon_realm from logins"; //NON-NLS
72  private static final String AUTOFILL_QUERY = "SELECT name, value, count, date_created " +
73  " FROM autofill, autofill_dates " +
74  " WHERE autofill.pair_id = autofill_dates.pair_id"
75  ; //NON-NLS
76  private static final String AUTOFILL_QUERY_V8X = "SELECT name, value, count, date_created, date_last_used from autofill"; //NON-NLS
77  private static final String WEBFORM_ADDRESS_QUERY = "SELECT first_name, middle_name, last_name, address_line_1, address_line_2, city, state, zipcode, country_code, number, email, date_modified " +
78  " FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
79  " WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
80 
81  private static final String WEBFORM_ADDRESS_QUERY_V8X = "SELECT first_name, middle_name, last_name, full_name, street_address, city, state, zipcode, country_code, number, email, date_modified, use_date, use_count" +
82  " FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
83  " WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
84  private final Logger logger = Logger.getLogger(this.getClass().getName());
85  private Content dataSource;
86  private IngestJobContext context;
87 
88  private static final Map<String, String> BROWSERS_MAP = ImmutableMap.<String, String>builder()
89  .put(NbBundle.getMessage(Chromium.class, "Browser.name.Microsoft.Edge"), "Microsoft/Edge")
90  .put(NbBundle.getMessage(Chromium.class, "Browser.name.Yandex"), "YandexBrowser")
91  .put(NbBundle.getMessage(Chromium.class, "Browser.name.Opera"), "Opera Software")
92  .put(NbBundle.getMessage(Chromium.class, "Browser.name.SalamWeb"), "SalamWeb")
93  .put(NbBundle.getMessage(Chromium.class, "Browser.name.UC.Browser"), "UCBrowser")
94  .put(NbBundle.getMessage(Chromium.class, "Browser.name.Brave"), "BraveSoftware")
95  .put(NbBundle.getMessage(Chromium.class, "Browser.name.Google.Chrome"), "Chrome")
96  .build();
97 
98 
99  @Messages({"# {0} - browserName",
100  "Progress_Message_Chrome_History=Chrome History Browser {0}",
101  "# {0} - browserName",
102  "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}",
103  "# {0} - browserName",
104  "Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}",
105  "# {0} - browserName",
106  "Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}",
107  "Progress_Message_Chrome_FormHistory=Chrome Form History",
108  "# {0} - browserName",
109  "Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}",
110  "# {0} - browserName",
111  "Progress_Message_Chrome_Logins=Chrome Logins Browser {0}",
112  "Progress_Message_Chrome_Cache=Chrome Cache",
113  })
114 
115  Chromium() {
116  moduleName = NbBundle.getMessage(Chromium.class, "Chrome.moduleName");
117  }
118 
119  @Override
120  public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
121  this.dataSource = dataSource;
122  this.context = context;
123  dataFound = false;
124 
125  for (Map.Entry<String, String> browser : BROWSERS_MAP.entrySet()) {
126  String browserName = browser.getKey();
127  String browserLocation = browser.getValue();
128  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName));
129  this.getHistory(browser.getKey(), browser.getValue());
130  if (context.dataSourceIngestIsCancelled()) {
131  return;
132  }
133 
134  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName));
135  this.getBookmark(browser.getKey(), browser.getValue());
136  if (context.dataSourceIngestIsCancelled()) {
137  return;
138  }
139 
140  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName));
141  this.getCookie(browser.getKey(), browser.getValue());
142  if (context.dataSourceIngestIsCancelled()) {
143  return;
144  }
145 
146  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName));
147  this.getLogins(browser.getKey(), browser.getValue());
148  if (context.dataSourceIngestIsCancelled()) {
149  return;
150  }
151 
152  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName));
153  this.getAutofill(browser.getKey(), browser.getValue());
154  if (context.dataSourceIngestIsCancelled()) {
155  return;
156  }
157 
158  progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName));
159  this.getDownload(browser.getKey(), browser.getValue());
160  if (context.dataSourceIngestIsCancelled()) {
161  return;
162  }
163  }
164 
165  progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
166  ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
167  chromeCacheExtractor.processCaches();
168 
169  }
170 
174  private void getHistory(String browser, String browserLocation) {
175  FileManager fileManager = currentCase.getServices().getFileManager();
176  List<AbstractFile> historyFiles;
177  try {
178  historyFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
179  } catch (TskCoreException ex) {
180  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles");
181  logger.log(Level.SEVERE, msg, ex);
182  this.addErrorMessage(this.getName() + ": " + msg);
183  return;
184  }
185 
186  // get only the allocated ones, for now
187  List<AbstractFile> allocatedHistoryFiles = new ArrayList<>();
188  for (AbstractFile historyFile : historyFiles) {
189  if (historyFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
190  allocatedHistoryFiles.add(historyFile);
191  }
192  }
193 
194  // log a message if we don't have any allocated history files
195  if (allocatedHistoryFiles.isEmpty()) {
196  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.couldntFindAnyFiles");
197  logger.log(Level.INFO, msg);
198  return;
199  }
200 
201  dataFound = true;
202  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
203  int j = 0;
204  while (j < allocatedHistoryFiles.size()) {
205  String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS
206  final AbstractFile historyFile = allocatedHistoryFiles.get(j++);
207  if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack"))
208  || (historyFile.getName().toLowerCase().contains("cache")) || (historyFile.getName().toLowerCase().contains("media"))
209  || (historyFile.getName().toLowerCase().contains("index"))) {
210  continue;
211  }
212  try {
213  ContentUtils.writeToFile(historyFile, new File(temps), context::dataSourceIngestIsCancelled);
214  } catch (ReadContentInputStreamException ex) {
215  logger.log(Level.WARNING, String.format("Error reading Chrome web history artifacts file '%s' (id=%d).",
216  historyFile.getName(), historyFile.getId()), ex); //NON-NLS
217  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errAnalyzingFile",
218  this.getName(), historyFile.getName()));
219  continue;
220  } catch (IOException ex) {
221  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome web history artifacts file '%s' (id=%d).",
222  temps, historyFile.getName(), historyFile.getId()), ex); //NON-NLS
223  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errAnalyzingFile",
224  this.getName(), historyFile.getName()));
225  continue;
226  }
227  File dbFile = new File(temps);
228  if (context.dataSourceIngestIsCancelled()) {
229  dbFile.delete();
230  break;
231  }
232  List<HashMap<String, Object>> tempList;
233  tempList = this.dbConnect(temps, HISTORY_QUERY);
234  logger.log(Level.INFO, "{0}- Now getting history from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
235  for (HashMap<String, Object> result : tempList) {
236  Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
237  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
238  RecentActivityExtracterModuleFactory.getModuleName(),
239  ((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
240  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
241  RecentActivityExtracterModuleFactory.getModuleName(),
242  (Long.valueOf(result.get("last_visit_time").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
243  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
244  RecentActivityExtracterModuleFactory.getModuleName(),
245  ((result.get("from_visit").toString() != null) ? result.get("from_visit").toString() : ""))); //NON-NLS
246  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
247  RecentActivityExtracterModuleFactory.getModuleName(),
248  ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
249  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
250  RecentActivityExtracterModuleFactory.getModuleName(), browser));
251  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
252  RecentActivityExtracterModuleFactory.getModuleName(),
253  (NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
254 
255  BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
256  if (bbart != null) {
257  bbartifacts.add(bbart);
258  }
259  }
260  dbFile.delete();
261  }
262 
263  if( !bbartifacts.isEmpty() ){
264  postArtifacts(bbartifacts);
265  }
266  }
267 
271  private void getBookmark(String browser, String browserLocation) {
272  FileManager fileManager = currentCase.getServices().getFileManager();
273  List<AbstractFile> bookmarkFiles;
274  try {
275  bookmarkFiles = fileManager.findFiles(dataSource, "%Bookmarks%", browserLocation); //NON-NLS
276  } catch (TskCoreException ex) {
277  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles");
278  logger.log(Level.SEVERE, msg, ex);
279  this.addErrorMessage(this.getName() + ": " + msg);
280  return;
281  }
282 
283  if (bookmarkFiles.isEmpty()) {
284  logger.log(Level.INFO, "Didn't find any Chrome bookmark files."); //NON-NLS
285  return;
286  }
287 
288  dataFound = true;
289  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
290  int j = 0;
291 
292  while (j < bookmarkFiles.size()) {
293  AbstractFile bookmarkFile = bookmarkFiles.get(j++);
294  if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))
295  || (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log"))
296  || (bookmarkFile.getName().toLowerCase().contains("backup")) || (bookmarkFile.getName().toLowerCase().contains("visualized"))
297  || (bookmarkFile.getName().toLowerCase().contains("bak")) || (bookmarkFile.getParentPath().toLowerCase().contains("backup"))) {
298  continue;
299  }
300  String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
301  try {
302  ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
303  } catch (ReadContentInputStreamException ex) {
304  logger.log(Level.WARNING, String.format("Error reading Chrome bookmark artifacts file '%s' (id=%d).",
305  bookmarkFile.getName(), bookmarkFile.getId()), ex); //NON-NLS
306  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile",
307  this.getName(), bookmarkFile.getName()));
308  continue;
309  } catch (IOException ex) {
310  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome bookmark artifacts file '%s' (id=%d).",
311  temps, bookmarkFile.getName(), bookmarkFile.getId()), ex); //NON-NLS
312  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile",
313  this.getName(), bookmarkFile.getName()));
314  continue;
315  }
316 
317  logger.log(Level.INFO, "{0}- Now getting Bookmarks from {1}", new Object[]{moduleName, temps}); //NON-NLS
318  File dbFile = new File(temps);
319  if (context.dataSourceIngestIsCancelled()) {
320  dbFile.delete();
321  break;
322  }
323 
324  FileReader tempReader;
325  try {
326  tempReader = new FileReader(temps);
327  } catch (FileNotFoundException ex) {
328  logger.log(Level.WARNING, "Error while trying to read into the Bookmarks for Chrome.", ex); //NON-NLS
329  continue;
330  }
331 
332  final JsonParser parser = new JsonParser();
333  JsonElement jsonElement;
334  JsonObject jElement, jRoot, jBookmark;
335  JsonArray jBookmarkArray;
336 
337  try {
338  jsonElement = parser.parse(tempReader);
339  jElement = jsonElement.getAsJsonObject();
340  jRoot = jElement.get("roots").getAsJsonObject(); //NON-NLS
341  jBookmark = jRoot.get("bookmark_bar").getAsJsonObject(); //NON-NLS
342  jBookmarkArray = jBookmark.getAsJsonArray("children"); //NON-NLS
343  } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
344  logger.log(Level.WARNING, "Error parsing Json from Chrome Bookmark.", ex); //NON-NLS
345  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile3",
346  this.getName(), bookmarkFile.getName()));
347  continue;
348  }
349 
350  for (JsonElement result : jBookmarkArray) {
351  JsonObject address = result.getAsJsonObject();
352  if (address == null) {
353  continue;
354  }
355  JsonElement urlEl = address.get("url"); //NON-NLS
356  String url;
357  if (urlEl != null) {
358  url = urlEl.getAsString();
359  } else {
360  url = "";
361  }
362  String name;
363  JsonElement nameEl = address.get("name"); //NON-NLS
364  if (nameEl != null) {
365  name = nameEl.getAsString();
366  } else {
367  name = "";
368  }
369  Long date;
370  JsonElement dateEl = address.get("date_added"); //NON-NLS
371  if (dateEl != null) {
372  date = dateEl.getAsLong();
373  } else {
374  date = Long.valueOf(0);
375  }
376  String domain = NetworkUtils.extractDomain(url);
377  try {
378  BlackboardArtifact bbart = bookmarkFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
379  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
380  //TODO Revisit usage of deprecated constructor as per TSK-583
381  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
382  RecentActivityExtracterModuleFactory.getModuleName(), url));
383  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
384  RecentActivityExtracterModuleFactory.getModuleName(), name));
385  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
386  RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600")));
387  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
388  RecentActivityExtracterModuleFactory.getModuleName(), browser));
389  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
390  RecentActivityExtracterModuleFactory.getModuleName(), domain));
391  bbart.addAttributes(bbattributes);
392 
393  bbartifacts.add(bbart);
394  } catch (TskCoreException ex) {
395  logger.log(Level.SEVERE, "Error while trying to insert Chrome bookmark artifact{0}", ex); //NON-NLS
396  this.addErrorMessage(
397  NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile4",
398  this.getName(), bookmarkFile.getName()));
399  }
400  }
401  postArtifacts(bbartifacts);
402  dbFile.delete();
403  }
404  }
405 
409  private void getCookie(String browser, String browserLocation) {
410 
411  FileManager fileManager = currentCase.getServices().getFileManager();
412  List<AbstractFile> cookiesFiles;
413  try {
414  cookiesFiles = fileManager.findFiles(dataSource, "%Cookies%", browserLocation); //NON-NLS
415  } catch (TskCoreException ex) {
416  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles");
417  logger.log(Level.SEVERE, msg, ex);
418  this.addErrorMessage(this.getName() + ": " + msg);
419  return;
420  }
421 
422  if (cookiesFiles.isEmpty()) {
423  logger.log(Level.INFO, "Didn't find any Chrome cookies files."); //NON-NLS
424  return;
425  }
426 
427  dataFound = true;
428  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
429  int j = 0;
430  while (j < cookiesFiles.size()) {
431  AbstractFile cookiesFile = cookiesFiles.get(j++);
432  if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) {
433  continue;
434  }
435  String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
436  try {
437  ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
438  } catch (ReadContentInputStreamException ex) {
439  logger.log(Level.WARNING, String.format("Error reading Chrome cookie artifacts file '%s' (id=%d).",
440  cookiesFile.getName(), cookiesFile.getId()), ex); //NON-NLS
441  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errAnalyzeFile",
442  this.getName(), cookiesFile.getName()));
443  continue;
444  } catch (IOException ex) {
445  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome cookie artifacts file '%s' (id=%d).",
446  temps, cookiesFile.getName(), cookiesFile.getId()), ex); //NON-NLS
447  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errAnalyzeFile",
448  this.getName(), cookiesFile.getName()));
449  continue;
450  }
451  File dbFile = new File(temps);
452  if (context.dataSourceIngestIsCancelled()) {
453  dbFile.delete();
454  break;
455  }
456 
457  List<HashMap<String, Object>> tempList = this.dbConnect(temps, COOKIE_QUERY);
458  logger.log(Level.INFO, "{0}- Now getting cookies from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
459  for (HashMap<String, Object> result : tempList) {
460  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
461  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
462  RecentActivityExtracterModuleFactory.getModuleName(),
463  ((result.get("host_key").toString() != null) ? result.get("host_key").toString() : ""))); //NON-NLS
464  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
465  RecentActivityExtracterModuleFactory.getModuleName(),
466  (Long.valueOf(result.get("last_access_utc").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
467 
468  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
469  RecentActivityExtracterModuleFactory.getModuleName(),
470  ((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //NON-NLS
471  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
472  RecentActivityExtracterModuleFactory.getModuleName(),
473  ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
474  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
475  RecentActivityExtracterModuleFactory.getModuleName(), browser));
476  String domain = result.get("host_key").toString(); //NON-NLS
477  domain = domain.replaceFirst("^\\.+(?!$)", "");
478  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
479  RecentActivityExtracterModuleFactory.getModuleName(), domain));
480 
481  BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
482  if (bbart != null) {
483  bbartifacts.add(bbart);
484  }
485  }
486 
487  dbFile.delete();
488  }
489 
490  if( !bbartifacts.isEmpty() ) {
491  postArtifacts(bbartifacts);
492  }
493  }
494 
498  private void getDownload(String browser, String browserLocation) {
499  FileManager fileManager = currentCase.getServices().getFileManager();
500  List<AbstractFile> downloadFiles;
501  try {
502  downloadFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
503  } catch (TskCoreException ex) {
504  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles");
505  logger.log(Level.SEVERE, msg, ex);
506  this.addErrorMessage(this.getName() + ": " + msg);
507  return;
508  }
509 
510  if (downloadFiles.isEmpty()) {
511  logger.log(Level.INFO, "Didn't find any Chrome download files."); //NON-NLS
512  return;
513  }
514 
515  dataFound = true;
516  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
517  int j = 0;
518  while (j < downloadFiles.size()) {
519  AbstractFile downloadFile = downloadFiles.get(j++);
520  if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack"))
521  || (downloadFile.getName().toLowerCase().contains("cache")) || (downloadFile.getName().toLowerCase().contains("index"))) {
522  continue;
523  }
524 
525  String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
526  try {
527  ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled);
528  } catch (ReadContentInputStreamException ex) {
529  logger.log(Level.WARNING, String.format("Error reading Chrome download artifacts file '%s' (id=%d).",
530  downloadFile.getName(), downloadFile.getId()), ex); //NON-NLS
531  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errAnalyzeFiles1",
532  this.getName(), downloadFile.getName()));
533  continue;
534  } catch (IOException ex) {
535  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome download artifacts file '%s' (id=%d).",
536  temps, downloadFile.getName(), downloadFile.getId()), ex); //NON-NLS
537  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errAnalyzeFiles1",
538  this.getName(), downloadFile.getName()));
539  continue;
540  }
541  File dbFile = new File(temps);
542  if (context.dataSourceIngestIsCancelled()) {
543  dbFile.delete();
544  break;
545  }
546 
547  List<HashMap<String, Object>> tempList;
548 
549  if (isChromePreVersion30(temps)) {
550  tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
551  } else {
552  tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V30);
553  }
554 
555  logger.log(Level.INFO, "{0}- Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
556  for (HashMap<String, Object> result : tempList) {
557  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
558  String fullPath = result.get("full_path").toString(); //NON-NLS
559  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
560  RecentActivityExtracterModuleFactory.getModuleName(), fullPath));
561  long pathID = Util.findID(dataSource, fullPath);
562  if (pathID != -1) {
563  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
564  NbBundle.getMessage(this.getClass(),
565  "Chrome.parentModuleName"), pathID));
566  }
567  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
568  RecentActivityExtracterModuleFactory.getModuleName(),
569  ((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
570  //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
571  Long time = (Long.valueOf(result.get("start_time").toString()) / 1000000) - Long.valueOf("11644473600"); //NON-NLS
572 
573  //TODO Revisit usage of deprecated constructor as per TSK-583
574  //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", time));
575  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
576  RecentActivityExtracterModuleFactory.getModuleName(), time));
577  String domain = NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""); //NON-NLS
578  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
579  RecentActivityExtracterModuleFactory.getModuleName(), domain));
580  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
581  RecentActivityExtracterModuleFactory.getModuleName(), browser));
582 
583  BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
584  if (webDownloadArtifact != null) {
585  bbartifacts.add(webDownloadArtifact);
586 
587  // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
588  try {
589  String normalizedFullPath = FilenameUtils.normalize(fullPath, true);
590  for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) {
591  BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
592  associatedObjectArtifact.addAttribute(
593  new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
594  RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
595 
596  bbartifacts.add(associatedObjectArtifact);
597  break;
598  }
599  } catch (TskCoreException ex) {
600  logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS
601  }
602  }
603  }
604 
605  dbFile.delete();
606  }
607 
608  if( !bbartifacts.isEmpty() ) {
609  postArtifacts(bbartifacts);
610  }
611  }
612 
616  private void getLogins(String browser, String browserLocation) {
617 
618  FileManager fileManager = currentCase.getServices().getFileManager();
619  List<AbstractFile> loginDataFiles;
620  try {
621  loginDataFiles = fileManager.findFiles(dataSource, "%Login Data%", browserLocation); //NON-NLS
622  } catch (TskCoreException ex) {
623  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
624  logger.log(Level.SEVERE, msg, ex);
625  this.addErrorMessage(this.getName() + ": " + msg);
626  return;
627  }
628 
629  if (loginDataFiles.isEmpty()) {
630  logger.log(Level.INFO, "Didn't find any Chrome Login Data files."); //NON-NLS
631  return;
632  }
633 
634  dataFound = true;
635  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
636  int j = 0;
637  while (j < loginDataFiles.size()) {
638  AbstractFile loginDataFile = loginDataFiles.get(j++);
639  if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) {
640  continue;
641  }
642  String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
643  try {
644  ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
645  } catch (ReadContentInputStreamException ex) {
646  logger.log(Level.WARNING, String.format("Error reading Chrome login artifacts file '%s' (id=%d).",
647  loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
648  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
649  this.getName(), loginDataFile.getName()));
650  continue;
651  } catch (IOException ex) {
652  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome login artifacts file '%s' (id=%d).",
653  temps, loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
654  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
655  this.getName(), loginDataFile.getName()));
656  continue;
657  }
658  File dbFile = new File(temps);
659  if (context.dataSourceIngestIsCancelled()) {
660  dbFile.delete();
661  break;
662  }
663  List<HashMap<String, Object>> tempList = this.dbConnect(temps, LOGIN_QUERY);
664  logger.log(Level.INFO, "{0}- Now getting login information from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
665  for (HashMap<String, Object> result : tempList) {
666  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
667 
668  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
669  RecentActivityExtracterModuleFactory.getModuleName(),
670  ((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : ""))); //NON-NLS
671 
672 
673  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
674  RecentActivityExtracterModuleFactory.getModuleName(),
675  (Long.valueOf(result.get("date_created").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
676 
677  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED,
678  RecentActivityExtracterModuleFactory.getModuleName(),
679  (NetworkUtils.extractDomain((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : "")))); //NON-NLS
680 
681  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
682  RecentActivityExtracterModuleFactory.getModuleName(),
683  ((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS
684 
685  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
686  RecentActivityExtracterModuleFactory.getModuleName(),
687  ((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
688 
689  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
690  RecentActivityExtracterModuleFactory.getModuleName(), browser));
691 
692  BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
693  if (bbart != null) {
694  bbartifacts.add(bbart);
695  }
696  }
697 
698  dbFile.delete();
699  }
700 
701  if( !bbartifacts.isEmpty() ) {
702  postArtifacts(bbartifacts);
703  }
704  }
705 
710  private void getAutofill(String browser, String browserLocation) {
711 
712  FileManager fileManager = currentCase.getServices().getFileManager();
713  List<AbstractFile> webDataFiles;
714  try {
715  webDataFiles = fileManager.findFiles(dataSource, "%Web Data%", browserLocation); //NON-NLS
716  } catch (TskCoreException ex) {
717  String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
718  logger.log(Level.SEVERE, msg, ex);
719  this.addErrorMessage(this.getName() + ": " + msg);
720  return;
721  }
722 
723  if (webDataFiles.isEmpty()) {
724  logger.log(Level.INFO, "Didn't find any Chrome Web Data files."); //NON-NLS
725  return;
726  }
727 
728  dataFound = true;
729  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
730  int j = 0;
731  while (j < webDataFiles.size()) {
732  AbstractFile webDataFile = webDataFiles.get(j++);
733  if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) {
734  continue;
735  }
736  String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
737  try {
738  ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
739  } catch (ReadContentInputStreamException ex) {
740  logger.log(Level.WARNING, String.format("Error reading Chrome Autofill artifacts file '%s' (id=%d).",
741  webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
742  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getAutofill.errMsg.errAnalyzingFiles",
743  this.getName(), webDataFile.getName()));
744  continue;
745  } catch (IOException ex) {
746  logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome Web data file '%s' (id=%d).",
747  tempFilePath, webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
748  this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
749  this.getName(), webDataFile.getName()));
750  continue;
751  }
752  File dbFile = new File(tempFilePath);
753  if (context.dataSourceIngestIsCancelled()) {
754  dbFile.delete();
755  break;
756  }
757 
758  // The DB schema is little different in schema version 8x vs older versions
759  boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
760 
761  // get form autofill artifacts
762  bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser));
763  try {
764  // get form address atifacts
765  getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
766  } catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) {
767  logger.log(Level.SEVERE, String.format("Error adding artifacts to the case database "
768  + "for chrome file %s [objId=%d]", webDataFile.getName(), webDataFile.getId()), ex);
769  }
770 
771  dbFile.delete();
772  }
773 
774  if( !bbartifacts.isEmpty() ){
775  postArtifacts(bbartifacts);
776  }
777  }
778 
788  private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X, String browser ) {
789 
790  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
791 
792  // The DB Schema is little different in version 8x vs older versions
793  String autoFillquery = (isSchemaV8X) ? AUTOFILL_QUERY_V8X
794  : AUTOFILL_QUERY;
795 
796  List<HashMap<String, Object>> autofills = this.dbConnect(dbFilePath, autoFillquery);
797  logger.log(Level.INFO, "{0}- Now getting Autofill information from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, autofills.size()}); //NON-NLS
798  for (HashMap<String, Object> result : autofills) {
799  Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
800 
801  // extract all common attributes
802  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
803  NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
804  ((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //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  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
811  RecentActivityExtracterModuleFactory.getModuleName(),
812  (Integer.valueOf(result.get("count").toString())))); //NON-NLS
813 
814  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
815  RecentActivityExtracterModuleFactory.getModuleName(),
816  Long.valueOf(result.get("date_created").toString()))); //NON-NLS
817 
818  // get schema version specific attributes
819  if (isSchemaV8X) {
820  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
821  RecentActivityExtracterModuleFactory.getModuleName(),
822  Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
823  }
824 
825  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
826  RecentActivityExtracterModuleFactory.getModuleName(), browser));
827 
828  // Add an artifact
829  BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
830  if (bbart != null) {
831  bbartifacts.add(bbart);
832  }
833  }
834 
835  // return all extracted artifacts
836  return bbartifacts;
837  }
838 
848  private void getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) throws NoCurrentCaseException,
849  TskCoreException, Blackboard.BlackboardException {
850 
851  String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
852  : WEBFORM_ADDRESS_QUERY;
853 
854  // Helper to create web form address artifacts.
855  WebBrowserArtifactsHelper helper = new WebBrowserArtifactsHelper(
856  Case.getCurrentCaseThrows().getSleuthkitCase(),
857  NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
858  webDataFile
859  );
860 
861  // Get Web form addresses
862  List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
863  logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
864  for (HashMap<String, Object> result : addresses) {
865 
866  // get name fields
867  String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
868  String middle_name = result.get("middle_name").toString() != null ? result.get("middle_name").toString() : "";
869  String last_name = result.get("last_name").toString() != null ? result.get("last_name").toString() : "";
870 
871  // get email and phone
872  String email_Addr = result.get("email").toString() != null ? result.get("email").toString() : "";
873  String phone_number = result.get("number").toString() != null ? result.get("number").toString() : "";
874 
875  // Get the address fields
876  String city = result.get("city").toString() != null ? result.get("city").toString() : "";
877  String state = result.get("state").toString() != null ? result.get("state").toString() : "";
878  String zipcode = result.get("zipcode").toString() != null ? result.get("zipcode").toString() : "";
879  String country_code = result.get("country_code").toString() != null ? result.get("country_code").toString() : "";
880 
881  // schema version specific fields
882  String full_name = "";
883  String street_address = "";
884  long date_modified = 0;
885  int use_count = 0;
886  long use_date = 0;
887 
888  if (isSchemaV8X) {
889  full_name = result.get("full_name").toString() != null ? result.get("full_name").toString() : "";
890  street_address = result.get("street_address").toString() != null ? result.get("street_address").toString() : "";
891  date_modified = result.get("date_modified").toString() != null ? Long.valueOf(result.get("date_modified").toString()) : 0;
892  use_count = result.get("use_count").toString() != null ? Integer.valueOf(result.get("use_count").toString()) : 0;
893  use_date = result.get("use_date").toString() != null ? Long.valueOf(result.get("use_date").toString()) : 0;
894  } else {
895  String address_line_1 = result.get("address_line_1").toString() != null ? result.get("street_address").toString() : "";
896  String address_line_2 = result.get("address_line_2").toString() != null ? result.get("address_line_2").toString() : "";
897  street_address = String.join(" ", address_line_1, address_line_2);
898  }
899 
900  // Create atrributes from extracted fields
901  if (full_name == null || full_name.isEmpty()) {
902  full_name = String.join(" ", first_name, middle_name, last_name);
903  }
904 
905  String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
906 
907  List<BlackboardAttribute> otherAttributes = new ArrayList<>();
908  if (date_modified > 0) {
909  otherAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
910  RecentActivityExtracterModuleFactory.getModuleName(),
911  date_modified)); //NON-NLS
912  }
913 
914  helper.addWebFormAddress(
915  full_name, email_Addr, phone_number,
916  locationAddress, 0, use_date,
917  use_count, otherAttributes);
918  }
919  }
920 
921  private boolean isChromePreVersion30(String temps) {
922  String query = "PRAGMA table_info(downloads)"; //NON-NLS
923  List<HashMap<String, Object>> columns = this.dbConnect(temps, query);
924  for (HashMap<String, Object> col : columns) {
925  if (col.get("name").equals("url")) { //NON-NLS
926  return true;
927  }
928  }
929 
930  return false;
931  }
932 }

Copyright © 2012-2020 Basis Technology. Generated on: Tue Sep 22 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.