Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractIE.java
Go to the documentation of this file.
1/*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 2012-2021 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 */
23package org.sleuthkit.autopsy.recentactivity;
24
25import java.io.BufferedReader;
26import org.openide.util.NbBundle;
27import org.sleuthkit.autopsy.coreutils.ExecUtil;
28import org.sleuthkit.autopsy.coreutils.NetworkUtils;
29import java.io.File;
30import java.io.FileInputStream;
31import java.io.FileNotFoundException;
32import java.io.IOException;
33import java.io.InputStreamReader;
34import java.nio.file.Paths;
35import java.text.ParseException;
36import java.text.SimpleDateFormat;
37import java.util.ArrayList;
38import java.util.List;
39import java.util.logging.Level;
40import org.sleuthkit.autopsy.coreutils.Logger;
41import java.util.Collection;
42import java.util.Scanner;
43import java.util.stream.Collectors;
44import org.openide.modules.InstalledFileLocator;
45import org.openide.util.NbBundle.Messages;
46import org.sleuthkit.autopsy.casemodule.services.FileManager;
47import org.sleuthkit.autopsy.datamodel.ContentUtils;
48import org.sleuthkit.datamodel.BlackboardArtifact;
49import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
50import org.sleuthkit.datamodel.BlackboardAttribute;
51import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
52import org.sleuthkit.datamodel.Content;
53import org.sleuthkit.autopsy.coreutils.PlatformUtil;
54import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
55import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
56import org.sleuthkit.autopsy.ingest.IngestJobContext;
57import org.sleuthkit.datamodel.AbstractFile;
58import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
59import org.sleuthkit.datamodel.ReadContentInputStream;
60import org.sleuthkit.datamodel.TskCoreException;
61
66class ExtractIE extends Extract {
67
68 private static final Logger logger = Logger.getLogger(ExtractIE.class.getName());
69 private String PASCO_LIB_PATH;
70 private final String JAVA_PATH;
71 private static final String RESOURCE_URL_PREFIX = "res://";
72 private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
73 private Content dataSource;
74 private final IngestJobContext context;
75
76 @Messages({
77 "Progress_Message_IE_History=IE History",
78 "Progress_Message_IE_Bookmarks=IE Bookmarks",
79 "Progress_Message_IE_Cookies=IE Cookies",
80 "Progress_Message_IE_Downloads=IE Downloads",
81 "Progress_Message_IE_FormHistory=IE Form History",
82 "Progress_Message_IE_AutoFill=IE Auto Fill",
83 "Progress_Message_IE_Logins=IE Logins",})
84
85 ExtractIE(IngestJobContext context) {
86 super(NbBundle.getMessage(ExtractIE.class, "ExtractIE.moduleName.text"), context);
87 JAVA_PATH = PlatformUtil.getJavaPath();
88 this.context = context;
89 }
90
91 @Override
92 public void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
93 String moduleTempDir = RAImageIngestModule.getRATempPath(getCurrentCase(), "IE", context.getJobId());
94 String moduleTempResultsDir = Paths.get(moduleTempDir, "results").toString();
95
96 this.dataSource = dataSource;
97 dataFound = false;
98
99 progressBar.progress(Bundle.Progress_Message_IE_Bookmarks());
100 this.getBookmark();
101
102 if (context.dataSourceIngestIsCancelled()) {
103 return;
104 }
105
106 progressBar.progress(Bundle.Progress_Message_IE_Cookies());
107 this.getCookie();
108
109 if (context.dataSourceIngestIsCancelled()) {
110 return;
111 }
112
113 progressBar.progress(Bundle.Progress_Message_IE_History());
114 this.getHistory(moduleTempDir, moduleTempResultsDir);
115 }
116
120 private void getBookmark() {
121 org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
122 List<AbstractFile> favoritesFiles;
123 try {
124 favoritesFiles = fileManager.findFiles(dataSource, "%.url", "Favorites"); //NON-NLS
125 } catch (TskCoreException ex) {
126 logger.log(Level.WARNING, "Error fetching 'url' files for Internet Explorer bookmarks.", ex); //NON-NLS
127 this.addErrorMessage(
128 NbBundle.getMessage(this.getClass(), "ExtractIE.getBookmark.errMsg.errGettingBookmarks",
129 this.getDisplayName()));
130 return;
131 }
132
133 if (favoritesFiles.isEmpty()) {
134 logger.log(Level.INFO, "Didn't find any IE bookmark files."); //NON-NLS
135 return;
136 }
137
138 dataFound = true;
139 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
140 for (AbstractFile fav : favoritesFiles) {
141 if (fav.getSize() == 0) {
142 continue;
143 }
144
145 if (context.dataSourceIngestIsCancelled()) {
146 break;
147 }
148
149 String url = getURLFromIEBookmarkFile(fav);
150
151 String name = fav.getName();
152 Long datetime = fav.getCrtime();
153 String Tempdate = datetime.toString();
154 datetime = Long.valueOf(Tempdate);
155 String domain = extractDomain(url);
156
157 try {
158 Collection<BlackboardAttribute> bbattributes = createBookmarkAttributes(
159 url,
160 name,
161 datetime,
162 NbBundle.getMessage(this.getClass(), "ExtractIE.moduleName.text"),
163 domain);
164
165 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, fav, bbattributes));
166 } catch (TskCoreException ex) {
167 logger.log(Level.SEVERE, String.format("Failed to create %s for file %d", ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getDisplayName(), fav.getId()), ex);
168 }
169 }
170
171 if (!context.dataSourceIngestIsCancelled()) {
172 postArtifacts(bbartifacts);
173 }
174 }
175
176 private String getURLFromIEBookmarkFile(AbstractFile fav) {
177 BufferedReader reader = new BufferedReader(new InputStreamReader(new ReadContentInputStream(fav)));
178 String line, url = "";
179 try {
180 line = reader.readLine();
181 while (null != line) {
182 // The actual shortcut line we are interested in is of the
183 // form URL=http://path/to/website
184 if (line.startsWith("URL")) { //NON-NLS
185 url = line.substring(line.indexOf("=") + 1);
186 break;
187 }
188 line = reader.readLine();
189 }
190 } catch (IOException ex) {
191 logger.log(Level.WARNING, "Failed to read from content: " + fav.getName(), ex); //NON-NLS
192 this.addErrorMessage(
193 NbBundle.getMessage(this.getClass(), "ExtractIE.getURLFromIEBmkFile.errMsg", this.getDisplayName(),
194 fav.getName()));
195 } catch (IndexOutOfBoundsException ex) {
196 logger.log(Level.WARNING, "Failed while getting URL of IE bookmark. Unexpected format of the bookmark file: " + fav.getName(), ex); //NON-NLS
197 this.addErrorMessage(
198 NbBundle.getMessage(this.getClass(), "ExtractIE.getURLFromIEBmkFile.errMsg2", this.getDisplayName(),
199 fav.getName()));
200 } finally {
201 try {
202 reader.close();
203 } catch (IOException ex) {
204 logger.log(Level.WARNING, "Failed to close reader.", ex); //NON-NLS
205 }
206 }
207
208 return url;
209 }
210
214 private void getCookie() {
215 org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
216 List<AbstractFile> cookiesFiles;
217 try {
218 cookiesFiles = fileManager.findFiles(dataSource, "%.txt", "Cookies"); //NON-NLS
219 } catch (TskCoreException ex) {
220 logger.log(Level.WARNING, "Error getting cookie files for IE"); //NON-NLS
221 this.addErrorMessage(
222 NbBundle.getMessage(this.getClass(), "ExtractIE.getCookie.errMsg.errGettingFile", this.getDisplayName()));
223 return;
224 }
225
226 if (cookiesFiles.isEmpty()) {
227 logger.log(Level.INFO, "Didn't find any IE cookies files."); //NON-NLS
228 return;
229 }
230
231 dataFound = true;
232 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
233 for (AbstractFile cookiesFile : cookiesFiles) {
234 if (context.dataSourceIngestIsCancelled()) {
235 break;
236 }
237 if (cookiesFile.getSize() == 0) {
238 continue;
239 }
240
241 byte[] t = new byte[(int) cookiesFile.getSize()];
242 try {
243 final int bytesRead = cookiesFile.read(t, 0, cookiesFile.getSize());
244 } catch (TskCoreException ex) {
245 logger.log(Level.WARNING, "Error reading bytes of Internet Explorer cookie.", ex); //NON-NLS
246 this.addErrorMessage(
247 NbBundle.getMessage(this.getClass(), "ExtractIE.getCookie.errMsg.errReadingIECookie",
248 this.getDisplayName(), cookiesFile.getName()));
249 continue;
250 }
251 String cookieString = new String(t);
252 String[] values = cookieString.split("\n");
253 String url = values.length > 2 ? values[2] : "";
254 String value = values.length > 1 ? values[1] : "";
255 String name = values.length > 0 ? values[0] : "";
256 Long datetime = cookiesFile.getCrtime();
257 String tempDate = datetime.toString();
258 datetime = Long.valueOf(tempDate);
259 String domain = extractDomain(url);
260
261 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
262 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
263 RecentActivityExtracterModuleFactory.getModuleName(), url));
264 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
265 RecentActivityExtracterModuleFactory.getModuleName(), datetime));
266 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
267 RecentActivityExtracterModuleFactory.getModuleName(), (name != null) ? name : ""));
268 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
269 RecentActivityExtracterModuleFactory.getModuleName(), value));
270 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
271 RecentActivityExtracterModuleFactory.getModuleName(),
272 NbBundle.getMessage(this.getClass(), "ExtractIE.moduleName.text")));
273 if (domain != null && domain.isEmpty() == false) {
274 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
275 RecentActivityExtracterModuleFactory.getModuleName(), domain));
276 }
277
278 try {
279 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_COOKIE, cookiesFile, bbattributes));
280 } catch (TskCoreException ex) {
281 logger.log(Level.SEVERE, String.format("Failed to create %s for file %d", BlackboardArtifact.Type.TSK_WEB_COOKIE.getDisplayName(), cookiesFile.getId()), ex);
282 }
283 }
284
285 if (!context.dataSourceIngestIsCancelled()) {
286 postArtifacts(bbartifacts);
287 }
288 }
289
297 private void getHistory(String moduleTempDir, String moduleTempResultsDir) {
298 logger.log(Level.INFO, "Pasco results path: {0}", moduleTempResultsDir); //NON-NLS
299 boolean foundHistory = false;
300
301 final File pascoRoot = InstalledFileLocator.getDefault().locate("pasco2", ExtractIE.class.getPackage().getName(), false); //NON-NLS
302 if (pascoRoot == null) {
303 this.addErrorMessage(
304 NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.unableToGetHist", this.getDisplayName()));
305 logger.log(Level.SEVERE, "Error finding pasco program "); //NON-NLS
306 return;
307 }
308
309 final String pascoHome = pascoRoot.getAbsolutePath();
310 logger.log(Level.INFO, "Pasco2 home: {0}", pascoHome); //NON-NLS
311
312 PASCO_LIB_PATH = pascoHome + File.separator + "pasco2.jar" + File.pathSeparator //NON-NLS
313 + pascoHome + File.separator + "*";
314
315 File resultsDir = new File(moduleTempResultsDir);
316 resultsDir.mkdirs();
317
318 // get index.dat files
319 FileManager fileManager = currentCase.getServices().getFileManager();
320 List<AbstractFile> indexFiles;
321 try {
322 indexFiles = fileManager.findFiles(dataSource, "index.dat"); //NON-NLS
323 } catch (TskCoreException ex) {
324 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errGettingHistFiles",
325 this.getDisplayName()));
326 logger.log(Level.WARNING, "Error fetching 'index.data' files for Internet Explorer history."); //NON-NLS
327 return;
328 }
329
330 if (indexFiles.isEmpty()) {
331 String msg = NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.noHistFiles");
332 logger.log(Level.INFO, msg);
333 return;
334 }
335
336 dataFound = true;
337 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
338 String temps;
339 String indexFileName;
340 for (AbstractFile indexFile : indexFiles) {
341 // Since each result represent an index.dat file,
342 // just create these files with the following notation:
343 // index<Number>.dat (i.e. index0.dat, index1.dat,..., indexN.dat)
344 // where <Number> is the obj_id of the file.
345 // Write each index.dat file to a temp directory.
346 //BlackboardArtifact bbart = fsc.newArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY);
347 indexFileName = "index" + Integer.toString((int) indexFile.getId()) + ".dat"; //NON-NLS
348 //indexFileName = "index" + Long.toString(bbart.getArtifactID()) + ".dat";
349 temps = moduleTempDir + File.separator + indexFileName; //NON-NLS
350 File datFile = new File(temps);
351 if (context.dataSourceIngestIsCancelled()) {
352 break;
353 }
354 try {
355 ContentUtils.writeToFile(indexFile, datFile, context::dataSourceIngestIsCancelled);
356 } catch (IOException e) {
357 logger.log(Level.WARNING, "Error while trying to write index.dat file " + datFile.getAbsolutePath(), e); //NON-NLS
358 this.addErrorMessage(
359 NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errWriteFile", this.getDisplayName(),
360 datFile.getAbsolutePath()));
361 continue;
362 }
363
364 String filename = "pasco2Result." + indexFile.getId() + ".txt"; //NON-NLS
365 boolean bPascProcSuccess = executePasco(temps, filename, moduleTempResultsDir);
366 if (context.dataSourceIngestIsCancelled()) {
367 return;
368 }
369
370 //At this point pasco2 proccessed the index files.
371 //Now fetch the results, parse them and the delete the files.
372 if (bPascProcSuccess) {
373 // Don't add TSK_OS_ACCOUNT artifacts to the ModuleDataEvent
374 bbartifacts.addAll(parsePascoOutput(indexFile, filename, moduleTempResultsDir).stream()
375 .filter(bbart -> bbart.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID())
376 .collect(Collectors.toList()));
377 if (context.dataSourceIngestIsCancelled()) {
378 return;
379 }
380 foundHistory = true;
381
382 //Delete index<n>.dat file since it was succcessfully by Pasco
383 datFile.delete();
384 } else {
385 logger.log(Level.WARNING, "pasco execution failed on: {0}", filename); //NON-NLS
386 this.addErrorMessage(
387 NbBundle.getMessage(this.getClass(), "ExtractIE.getHistory.errMsg.errProcHist", this.getDisplayName()));
388 }
389 }
390
391 if (!context.dataSourceIngestIsCancelled()) {
392 postArtifacts(bbartifacts);
393 }
394 }
395
405 @Messages({
406 "# {0} - sub module name",
407 "ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history",})
408 private boolean executePasco(String indexFilePath, String outputFileName, String moduleTempResultsDir) {
409 boolean success = true;
410 try {
411 final String outputFileFullPath = moduleTempResultsDir + File.separator + outputFileName;
412 final String errFileFullPath = moduleTempResultsDir + File.separator + outputFileName + ".err"; //NON-NLS
413 logger.log(Level.INFO, "Writing pasco results to: {0}", outputFileFullPath); //NON-NLS
414 List<String> commandLine = new ArrayList<>();
415 commandLine.add(JAVA_PATH);
416 commandLine.add("--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.dom=ALL-UNNAMED");
417 commandLine.add("-cp"); //NON-NLS
418 commandLine.add(PASCO_LIB_PATH);
419 commandLine.add("isi.pasco2.Main"); //NON-NLS
420 commandLine.add("-T"); //NON-NLS
421 commandLine.add("history"); //NON-NLS
422 commandLine.add(indexFilePath);
423 ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
424 processBuilder.redirectOutput(new File(outputFileFullPath));
425 processBuilder.redirectError(new File(errFileFullPath));
426 /*
427 * NOTE on Pasco return codes: There is no documentation for Pasco.
428 * Looking at the Pasco source code I see that when something goes
429 * wrong Pasco returns a negative number as a return code. However,
430 * we should still attempt to parse the Pasco output even if that
431 * happens. I have seen many situations where Pasco output file
432 * contains a lot of useful data and only the last entry is
433 * corrupted.
434 */
435 ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
436 // @@@ Investigate use of history versus cache as type.
437 } catch (IOException ex) {
438 logger.log(Level.SEVERE, "Error executing Pasco to process Internet Explorer web history", ex); //NON-NLS
439 addErrorMessage(Bundle.ExtractIE_executePasco_errMsg_errorRunningPasco(getDisplayName()));
440 success = false;
441 }
442 return success;
443 }
444
455 private Collection<BlackboardArtifact> parsePascoOutput(AbstractFile origFile, String pascoOutputFileName, String moduleTempResultsDir) {
456
457 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
458 String fnAbs = moduleTempResultsDir + File.separator + pascoOutputFileName;
459
460 File file = new File(fnAbs);
461 if (file.exists() == false) {
462 this.addErrorMessage(
463 NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.notFound", this.getDisplayName(),
464 file.getName()));
465 logger.log(Level.WARNING, "Pasco Output not found: {0}", file.getPath()); //NON-NLS
466 return bbartifacts;
467 }
468
469 // Make sure the file the is not empty or the Scanner will
470 // throw a "No Line found" Exception
471 if (file.length() == 0) {
472 return bbartifacts;
473 }
474
475 Scanner fileScanner;
476 try {
477 fileScanner = new Scanner(new FileInputStream(file.toString()));
478 } catch (FileNotFoundException ex) {
479 this.addErrorMessage(
480 NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.errParsing", this.getDisplayName(),
481 file.getName()));
482 logger.log(Level.WARNING, "Unable to find the Pasco file at " + file.getPath(), ex); //NON-NLS
483 return bbartifacts;
484 }
485 while (fileScanner.hasNext()) {
486
487 if (context.dataSourceIngestIsCancelled()) {
488 return bbartifacts;
489 }
490
491 String line = fileScanner.nextLine();
492 if (!line.startsWith("URL")) { //NON-NLS
493 continue;
494 }
495
496 String[] lineBuff = line.split("\\t"); //NON-NLS
497
498 if (lineBuff.length < 4) {
499 logger.log(Level.INFO, "Found unrecognized IE history format."); //NON-NLS
500 continue;
501 }
502
503 String actime = lineBuff[3];
504 Long ftime = (long) 0;
505 String user = "";
506 String realurl = null;
507 String domain;
508
509 /*
510 * We've seen two types of lines: URL http://XYZ.com .... URL
511 * Visited: Joe@http://XYZ.com ....
512 */
513 if (lineBuff[1].contains("@")) {
514 String url[] = lineBuff[1].split("@", 2);
515
516 /*
517 * Verify the left portion of the URL is valid.
518 */
519 domain = extractDomain(url[0]);
520
521 if (domain != null && domain.isEmpty() == false) {
522 /*
523 * Use the entire input for the URL.
524 */
525 realurl = lineBuff[1].trim();
526 } else {
527 /*
528 * Use the left portion of the input for the user, and the
529 * right portion for the host.
530 */
531 user = url[0];
532 user = user.replace("Visited:", ""); //NON-NLS
533 user = user.replace(":Host:", ""); //NON-NLS
534 user = user.replaceAll("(:)(.*?)(:)", "");
535 user = user.trim();
536 realurl = url[1];
537 realurl = realurl.replace("Visited:", ""); //NON-NLS
538 realurl = realurl.replaceAll(":(.*?):", "");
539 realurl = realurl.replace(":Host:", ""); //NON-NLS
540 realurl = realurl.trim();
541 domain = extractDomain(realurl);
542 }
543 } else {
544 /*
545 * Use the entire input for the URL.
546 */
547 realurl = lineBuff[1].trim();
548 domain = extractDomain(realurl);
549 }
550
551 if (!actime.isEmpty()) {
552 try {
553 Long epochtime = dateFormatter.parse(actime).getTime();
554 ftime = epochtime / 1000;
555 } catch (ParseException e) {
556 this.addErrorMessage(
557 NbBundle.getMessage(this.getClass(), "ExtractIE.parsePascoOutput.errMsg.errParsingEntry",
558 this.getDisplayName()));
559 logger.log(Level.WARNING, String.format("Error parsing Pasco results, may have partial processing of corrupt file (id=%d)", origFile.getId()), e); //NON-NLS
560 }
561 }
562
563 try {
564 Collection<BlackboardAttribute> bbattributes = createHistoryAttributes(
565 realurl,
566 ftime,
567 null,
568 null,
569 NbBundle.getMessage(this.getClass(), "ExtractIE.moduleName.text"),
570 domain,
571 user);
572
573 bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_HISTORY, origFile, bbattributes));
574 } catch (TskCoreException ex) {
575 logger.log(Level.SEVERE, String.format("Failed to create %s for file %d", BlackboardArtifact.Type.TSK_WEB_HISTORY.getDisplayName(), origFile.getId()), ex);
576 }
577 }
578 fileScanner.close();
579 return bbartifacts;
580 }
581
590 private String extractDomain(String url) {
591 if (url == null || url.isEmpty()) {
592 return url;
593 }
594
595 if (url.toLowerCase().startsWith(RESOURCE_URL_PREFIX)) {
596 /*
597 * Ignore URLs that begin with the matched text.
598 */
599 return null;
600 }
601
602 return NetworkUtils.extractDomain(url);
603 }
604}
List< AbstractFile > findFiles(String fileName)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.