19 package org.sleuthkit.autopsy.recentactivity;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.text.DateFormat;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Scanner;
37 import java.util.logging.Level;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40 import org.openide.modules.InstalledFileLocator;
41 import org.openide.util.NbBundle.Messages;
53 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK;
54 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE;
55 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
62 final class ExtractEdge
extends Extract {
64 private static final Logger LOG = Logger.getLogger(ExtractEdge.class.getName());
65 private Content dataSource;
66 private final IngestJobContext context;
67 private HashMap<String, ArrayList<String>> containersTable;
69 private static final String EDGE =
"Edge";
71 private static final String EDGE_KEYWORD_VISIT =
"Visited:";
72 private static final String IGNORE_COMMA_IN_QUOTES_REGEX =
",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";
74 private static final String EDGE_TABLE_TYPE_DOWNLOAD =
"iedownload";
75 private static final String EDGE_TABLE_TYPE_HISTORY =
"History";
76 private static final String EDGE_TABLE_TYPE_COOKIE =
"cookie";
78 private static final String EDGE_HEAD_URL =
"url";
79 private static final String EDGE_HEAD_ACCESSTIME =
"accessedtime";
80 private static final String EDGE_HEAD_NAME =
"name";
81 private static final String EDGE_HEAD_CONTAINER_ID =
"containerid";
82 private static final String EDGE_HEAD_RESPONSEHEAD =
"responseheaders";
83 private static final String EDGE_HEAD_TITLE =
"title";
84 private static final String EDGE_HEAD_RDOMAIN =
"rdomain";
85 private static final String EDGE_HEAD_VALUE =
"value";
86 private static final String EDGE_HEAD_LASTMOD =
"lastmodified";
88 private static final String EDGE_WEBCACHE_PREFIX =
"WebCacheV01";
89 private static final String EDGE_CONTAINER_FILE_PREFIX =
"Container_";
90 private static final String EDGE_CONTAINER_FILE_EXT =
".csv";
91 private static final String EDGE_WEBCACHE_EXT =
".dat";
93 private static final String ESE_TOOL_NAME =
"ESEDatabaseView.exe";
94 private static final String EDGE_WEBCACHE_NAME =
"WebCacheV01.dat";
95 private static final String EDGE_SPARTAN_NAME =
"Spartan.edb";
96 private static final String EDGE_CONTAINTERS_FILE_NAME =
"Containers.csv";
97 private static final String EDGE_FAVORITE_FILE_NAME =
"Favorites.csv";
98 private static final String EDGE_OUTPUT_FILE_NAME =
"Output.txt";
99 private static final String EDGE_ERROR_FILE_NAME =
"File.txt";
100 private static final String EDGE_WEBCACHE_FOLDER_NAME =
"WebCache";
101 private static final String EDGE_SPARTAN_FOLDER_NAME =
"MicrosoftEdge";
103 private static final String ESE_TOOL_FOLDER =
"ESEDatabaseView";
104 private static final String EDGE_RESULT_FOLDER_NAME =
"results";
108 private SimpleDateFormat previouslyValidDateFormat = null;
111 "ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer",
112 "ExtractEdge_process_errMsg_errGettingWebCacheFiles=Error trying to retrieving Edge WebCacheV01 file",
113 "ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file",
114 "ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan.edb file",
115 "ExtractEdge_Module_Name=Microsoft Edge Analyzer",
116 "ExtractEdge_getHistory_containerFileNotFound=Error while trying to analyze Edge history",
117 "Progress_Message_Edge_History=Microsoft Edge History",
118 "Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks",
119 "Progress_Message_Edge_Cookies=Microsoft Edge Cookies",})
124 ExtractEdge(IngestJobContext context) {
125 super(Bundle.ExtractEdge_Module_Name(), context);
126 this.context = context;
130 protected String getDisplayName() {
131 return Bundle.ExtractEdge_Module_Name();
135 void process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
136 String moduleTempDir = RAImageIngestModule.getRATempPath(getCurrentCase(), EDGE, context.getJobId());
137 String moduleTempResultDir = Paths.get(moduleTempDir, EDGE_RESULT_FOLDER_NAME).toString();
139 this.dataSource = dataSource;
140 this.setFoundData(
false);
142 List<AbstractFile> webCacheFiles = null;
143 List<AbstractFile> spartanFiles = null;
146 webCacheFiles = fetchWebCacheDBFiles();
147 }
catch (TskCoreException ex) {
148 this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_errGettingWebCacheFiles());
149 LOG.log(Level.SEVERE,
"Error fetching 'WebCacheV01.dat' files for Microsoft Edge", ex);
152 if (context.dataSourceIngestIsCancelled()) {
157 spartanFiles = fetchSpartanDBFiles();
158 }
catch (TskCoreException ex) {
159 this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_spartanFail());
160 LOG.log(Level.SEVERE,
"Error fetching 'spartan.edb' files for Microsoft Edge", ex);
164 if (webCacheFiles == null && spartanFiles == null) {
168 this.setFoundData(
true);
170 if (!PlatformUtil.isWindowsOS()) {
171 LOG.log(Level.WARNING,
"Microsoft Edge files found, unable to parse on Non-Windows system");
175 if (context.dataSourceIngestIsCancelled()) {
179 final String esedumper = getPathForESEDumper();
180 if (esedumper == null) {
181 LOG.log(Level.SEVERE,
"Error finding ESEDatabaseViewer program");
182 this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_unableFindESEViewer());
187 this.processWebCacheDbFile(esedumper, webCacheFiles, progressBar, moduleTempDir, moduleTempResultDir);
188 }
catch (IOException | TskCoreException ex) {
189 LOG.log(Level.SEVERE,
"Error processing 'WebCacheV01.dat' files for Microsoft Edge", ex);
190 this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_webcacheFail());
193 progressBar.progress(Bundle.Progress_Message_Edge_Bookmarks());
195 this.processSpartanDbFile(esedumper, spartanFiles, moduleTempDir, moduleTempResultDir);
196 }
catch (IOException | TskCoreException ex) {
197 LOG.log(Level.SEVERE,
"Error processing 'spartan.edb' files for Microsoft Edge", ex);
198 this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_spartanFail());
214 void processWebCacheDbFile(String eseDumperPath, List<AbstractFile> webCacheFiles, DataSourceIngestModuleProgress progressBar,
215 String moduleTempDir, String moduleTempResultDir)
throws IOException, TskCoreException {
216 for (AbstractFile webCacheFile : webCacheFiles) {
218 if (context.dataSourceIngestIsCancelled()) {
222 clearContainerTable();
225 String tempWebCacheFileName = EDGE_WEBCACHE_PREFIX
226 + Integer.toString((
int) webCacheFile.getId()) + EDGE_WEBCACHE_EXT;
227 File tempWebCacheFile =
new File(moduleTempDir, tempWebCacheFileName);
230 ContentUtils.writeToFile(webCacheFile, tempWebCacheFile,
231 context::dataSourceIngestIsCancelled);
232 }
catch (IOException ex) {
233 throw new IOException(
"Error writingToFile: " + webCacheFile, ex);
236 File resultsDir =
new File(moduleTempDir, Integer.toString((
int) webCacheFile.getId()));
239 executeDumper(eseDumperPath, tempWebCacheFile.getAbsolutePath(),
240 resultsDir.getAbsolutePath());
242 if (context.dataSourceIngestIsCancelled()) {
246 progressBar.progress(Bundle.Progress_Message_Edge_History());
248 this.getHistory(webCacheFile, resultsDir);
250 if (context.dataSourceIngestIsCancelled()) {
254 progressBar.progress(Bundle.Progress_Message_Edge_Cookies());
256 this.getCookies(webCacheFile, resultsDir);
259 tempWebCacheFile.delete();
260 FileUtil.deleteFileDir(resultsDir);
277 void processSpartanDbFile(String eseDumperPath, List<AbstractFile> spartanFiles, String moduleTempDir, String moduleTempResultDir)
throws IOException, TskCoreException {
278 for (AbstractFile spartanFile : spartanFiles) {
280 if (context.dataSourceIngestIsCancelled()) {
285 String tempSpartanFileName = EDGE_WEBCACHE_PREFIX
286 + Integer.toString((
int) spartanFile.getId()) + EDGE_WEBCACHE_EXT;
287 File tempSpartanFile =
new File(moduleTempDir, tempSpartanFileName);
290 ContentUtils.writeToFile(spartanFile, tempSpartanFile,
291 context::dataSourceIngestIsCancelled);
292 }
catch (IOException ex) {
293 throw new IOException(
"Error writingToFile: " + spartanFile, ex);
296 File resultsDir =
new File(moduleTempResultDir, Integer.toString((
int) spartanFile.getId()));
299 executeDumper(eseDumperPath, tempSpartanFile.getAbsolutePath(),
300 resultsDir.getAbsolutePath());
302 if (context.dataSourceIngestIsCancelled()) {
306 this.getBookmarks(spartanFile, resultsDir);
309 tempSpartanFile.delete();
310 FileUtil.deleteFileDir(resultsDir);
326 private void getHistory(AbstractFile origFile, File resultDir)
throws TskCoreException, FileNotFoundException {
327 ArrayList<File> historyFiles = getHistoryFiles(resultDir);
328 if (historyFiles == null) {
332 for (File file : historyFiles) {
333 if (context.dataSourceIngestIsCancelled()) {
339 fileScanner =
new Scanner(
new FileInputStream(file.toString()));
340 }
catch (FileNotFoundException ex) {
341 LOG.log(Level.WARNING,
"Unable to find the ESEDatabaseView file at " + file.getPath(), ex);
345 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
348 List<String> headers = null;
349 while (fileScanner.hasNext()) {
350 if (context.dataSourceIngestIsCancelled()) {
354 String line = fileScanner.nextLine();
355 if (headers == null) {
356 headers = Arrays.asList(line.toLowerCase().split(
","));
360 if (line.contains(EDGE_KEYWORD_VISIT)) {
361 BlackboardArtifact ba = getHistoryArtifact(origFile, headers, line);
371 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
372 postArtifacts(bbartifacts);
386 private void getBookmarks(AbstractFile origFile, File resultDir)
throws TskCoreException {
388 File favoriteFile =
new File(resultDir, EDGE_FAVORITE_FILE_NAME);
391 fileScanner =
new Scanner(
new FileInputStream(favoriteFile));
392 }
catch (FileNotFoundException ex) {
398 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
401 List<String> headers = null;
402 while (fileScanner.hasNext()) {
403 String line = fileScanner.nextLine();
404 if (headers == null) {
405 headers = Arrays.asList(line.toLowerCase().split(
","));
409 BlackboardArtifact ba = getBookmarkArtifact(origFile, headers, line);
418 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
419 postArtifacts(bbartifacts);
431 private void getCookies(AbstractFile origFile, File resultDir)
throws TskCoreException {
432 File containerFiles[] = resultDir.listFiles((dir, name) -> name.toLowerCase().contains(EDGE_TABLE_TYPE_COOKIE));
434 if (containerFiles == null) {
438 for (File file : containerFiles) {
439 if (context.dataSourceIngestIsCancelled()) {
445 fileScanner =
new Scanner(
new FileInputStream(file.toString()));
446 }
catch (FileNotFoundException ex) {
447 LOG.log(Level.WARNING,
"Unable to find the ESEDatabaseView file at " + file.getPath(), ex);
451 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
454 List<String> headers = null;
455 while (fileScanner.hasNext()) {
456 if (context.dataSourceIngestIsCancelled()) {
460 String line = fileScanner.nextLine();
461 if (headers == null) {
462 headers = Arrays.asList(line.toLowerCase().split(
","));
466 BlackboardArtifact ba = getCookieArtifact(origFile, headers, line);
475 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
476 postArtifacts(bbartifacts);
492 private void getDownloads(AbstractFile origFile, File resultDir)
throws TskCoreException, FileNotFoundException {
493 ArrayList<File> downloadFiles = getDownloadFiles(resultDir);
495 if (downloadFiles == null) {
499 for (File file : downloadFiles) {
500 if (context.dataSourceIngestIsCancelled()) {
506 fileScanner =
new Scanner(
new FileInputStream(file.toString()));
507 }
catch (FileNotFoundException ex) {
508 LOG.log(Level.WARNING,
"Unable to find the ESEDatabaseView file at " + file.getPath(), ex);
511 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
514 List<String> headers = null;
515 while (fileScanner.hasNext()) {
516 if (context.dataSourceIngestIsCancelled()) {
520 String line = fileScanner.nextLine();
521 if (headers == null) {
522 headers = Arrays.asList(line.toLowerCase().split(
","));
526 if (line.contains(EDGE_TABLE_TYPE_DOWNLOAD)) {
528 BlackboardArtifact ba = getDownloadArtifact(origFile, headers, line);
538 if (!context.dataSourceIngestIsCancelled()) {
539 postArtifacts(bbartifacts);
550 private String getPathForESEDumper() {
551 Path path = Paths.get(ESE_TOOL_FOLDER, ESE_TOOL_NAME);
552 File eseToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
553 ExtractEdge.class.getPackage().getName(),
false);
554 if (eseToolFile != null) {
555 return eseToolFile.getAbsolutePath();
568 private List<AbstractFile> fetchWebCacheDBFiles() throws TskCoreException {
570 = currentCase.getServices().getFileManager();
571 return fileManager.
findFiles(dataSource, EDGE_WEBCACHE_NAME, EDGE_WEBCACHE_FOLDER_NAME);
581 private List<AbstractFile> fetchSpartanDBFiles() throws TskCoreException {
583 = currentCase.getServices().getFileManager();
584 return fileManager.
findFiles(dataSource, EDGE_SPARTAN_NAME, EDGE_SPARTAN_FOLDER_NAME);
599 private void executeDumper(String dumperPath, String inputFilePath,
600 String outputDir)
throws IOException {
602 final Path outputFilePath = Paths.get(outputDir, EDGE_OUTPUT_FILE_NAME);
603 final Path errFilePath = Paths.get(outputDir, EDGE_ERROR_FILE_NAME);
604 LOG.log(Level.INFO,
"Writing ESEDatabaseViewer results to: {0}", outputDir);
606 List<String> commandLine =
new ArrayList<>();
607 commandLine.add(dumperPath);
608 commandLine.add(
"/table");
609 commandLine.add(inputFilePath);
610 commandLine.add(
"*");
611 commandLine.add(
"/scomma");
612 commandLine.add(outputDir +
"\\" +
"*.csv");
614 ProcessBuilder processBuilder =
new ProcessBuilder(commandLine);
615 processBuilder.redirectOutput(outputFilePath.toFile());
616 processBuilder.redirectError(errFilePath.toFile());
618 ExecUtil.execute(processBuilder,
new DataSourceIngestModuleProcessTerminator(context,
true));
633 private BlackboardArtifact getHistoryArtifact(AbstractFile origFile, List<String> headers, String line)
throws TskCoreException {
634 String[] rowSplit = line.split(
",");
636 int index = headers.indexOf(EDGE_HEAD_URL);
637 String urlUserStr = rowSplit[index];
639 String[] str = urlUserStr.split(
"@");
640 String user = (str[0].replace(EDGE_KEYWORD_VISIT,
"")).trim();
643 index = headers.indexOf(EDGE_HEAD_ACCESSTIME);
644 String accessTime = rowSplit[index].trim();
645 Long ftime = parseTimestamp(accessTime);
647 return createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_HISTORY, origFile, createHistoryAttributes(url, ftime,
649 this.getDisplayName(),
650 NetworkUtils.extractDomain(url), user));
664 private BlackboardArtifact getCookieArtifact(AbstractFile origFile, List<String> headers, String line)
throws TskCoreException {
665 String[] lineSplit = line.split(
",");
667 String accessTime = lineSplit[headers.indexOf(EDGE_HEAD_LASTMOD)].trim();
668 Long ftime = parseTimestamp(accessTime);
670 String domain = lineSplit[headers.indexOf(EDGE_HEAD_RDOMAIN)].trim();
671 String name = hexToChar(lineSplit[headers.indexOf(EDGE_HEAD_NAME)].trim());
672 String value = hexToChar(lineSplit[headers.indexOf(EDGE_HEAD_VALUE)].trim());
673 String url = flipDomain(domain);
675 return createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_COOKIE, origFile, createCookieAttributes(url, null, ftime, null, name, value, this.getDisplayName(), NetworkUtils.extractDomain(url)));
693 private BlackboardArtifact getDownloadArtifact(AbstractFile origFile, List<String> headers, String line)
throws TskCoreException {
694 BlackboardArtifact bbart = null;
696 String[] lineSplit = line.split(
",");
697 String rheader = lineSplit[headers.indexOf(EDGE_HEAD_RESPONSEHEAD)];
717 private BlackboardArtifact getBookmarkArtifact(AbstractFile origFile, List<String> headers, String line)
throws TskCoreException {
719 String[] lineSplit = line.split(IGNORE_COMMA_IN_QUOTES_REGEX, -1);
721 String url = lineSplit[headers.indexOf(EDGE_HEAD_URL)];
722 String title = lineSplit[headers.indexOf(EDGE_HEAD_TITLE)].replace(
"\"",
"");
728 return createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, origFile, createBookmarkAttributes(url, title, null,
729 this.getDisplayName(), NetworkUtils.extractDomain(url)));
746 private Long parseTimestamp(String timeStr) {
749 if (previouslyValidDateFormat != null) {
751 return previouslyValidDateFormat.parse(timeStr).getTime() / 1000;
752 }
catch (ParseException ex) {
759 SimpleDateFormat usDateFormat =
new SimpleDateFormat(
"MM/dd/yyyy hh:mm:ss a");
760 usDateFormat.setLenient(
false);
761 Long epochTime = usDateFormat.parse(timeStr).getTime();
762 previouslyValidDateFormat = usDateFormat;
763 return epochTime / 1000;
764 }
catch (ParseException ex) {
770 boolean monthFirstFromLocale =
true;
771 String localeDatePattern = ((SimpleDateFormat) DateFormat.getDateInstance(
772 DateFormat.SHORT, Locale.getDefault())).toPattern();
773 if (localeDatePattern.startsWith(
"d")) {
774 monthFirstFromLocale =
false;
779 boolean monthFirst = monthFirstFromLocale;
780 Pattern pattern = Pattern.compile(
"^([0-9]{1,2})[^0-9]([0-9]{1,2})");
781 Matcher matcher = pattern.matcher(timeStr);
782 if (matcher.find()) {
783 int firstVal = Integer.parseInt(matcher.group(1));
784 int secondVal = Integer.parseInt(matcher.group(2));
788 }
else if (secondVal > 12) {
795 boolean hasAmPm =
false;
796 if (timeStr.endsWith(
"M") || timeStr.endsWith(
"m")) {
801 boolean hasSlashes =
false;
802 if (timeStr.contains(
"/")) {
807 String dateFormatPattern;
810 dateFormatPattern =
"MM/dd/yyyy ";
812 dateFormatPattern =
"MM.dd.yyyy ";
816 dateFormatPattern =
"dd/MM/yyyy ";
818 dateFormatPattern =
"dd.MM.yyyy ";
823 dateFormatPattern +=
"hh:mm:ss a";
825 dateFormatPattern +=
"HH:mm:ss";
829 SimpleDateFormat dateFormat =
new SimpleDateFormat(dateFormatPattern);
830 dateFormat.setLenient(
false);
831 Long epochTime = dateFormat.parse(timeStr).getTime();
832 previouslyValidDateFormat = dateFormat;
833 return epochTime / 1000;
834 }
catch (ParseException ex) {
835 LOG.log(Level.WARNING,
"Timestamp could not be parsed ({0})", timeStr);
847 private String hexToChar(String hexString) {
848 String[] hexValues = hexString.split(
" ");
849 StringBuilder output =
new StringBuilder();
851 for (String str : hexValues) {
853 int value = Integer.parseInt(str, 16);
855 output.append((
char) value);
857 }
catch (NumberFormatException ex) {
862 return output.toString();
877 private String flipDomain(String domain) {
878 if (domain == null || domain.isEmpty()) {
882 String[] tokens = domain.split(
"\\.");
884 if (tokens.length < 2 || tokens.length > 3) {
888 StringBuilder buf =
new StringBuilder();
889 if (tokens.length > 2) {
890 buf.append(tokens[2]);
893 buf.append(tokens[1]);
895 buf.append(tokens[0]);
897 return buf.toString();
908 private ArrayList<File> getDownloadFiles(File resultDir)
throws FileNotFoundException {
909 return getContainerFiles(resultDir, EDGE_TABLE_TYPE_DOWNLOAD);
921 private ArrayList<File> getHistoryFiles(File resultDir)
throws FileNotFoundException {
922 return getContainerFiles(resultDir, EDGE_TABLE_TYPE_HISTORY);
936 private ArrayList<File> getContainerFiles(File resultDir, String type)
throws FileNotFoundException {
937 HashMap<String, ArrayList<String>> idTable = getContainerIDTable(resultDir);
939 ArrayList<String> idList = idTable.get(type);
940 if (idList == null) {
944 ArrayList<File> fileList =
new ArrayList<>();
945 for (String str : idList) {
946 String fileName = EDGE_CONTAINER_FILE_PREFIX + str + EDGE_CONTAINER_FILE_EXT;
947 fileList.add(
new File(resultDir, fileName));
965 private HashMap<String, ArrayList<String>> getContainerIDTable(File resultDir)
throws FileNotFoundException {
967 if (containersTable == null) {
968 File containerFile =
new File(resultDir, EDGE_CONTAINTERS_FILE_NAME);
970 try (Scanner fileScanner =
new Scanner(
new FileInputStream(containerFile))) {
971 List<String> headers = null;
972 containersTable =
new HashMap<>();
975 while (fileScanner.hasNext()) {
976 String line = fileScanner.nextLine();
977 if (headers == null) {
978 headers = Arrays.asList(line.toLowerCase().split(
","));
979 nameIdx = headers.indexOf(EDGE_HEAD_NAME);
980 idIdx = headers.indexOf(EDGE_HEAD_CONTAINER_ID);
982 String[] row = line.split(
",");
983 String name = row[nameIdx];
984 String
id = row[idIdx];
986 ArrayList<String> idList = containersTable.get(name);
987 if (idList == null) {
988 idList =
new ArrayList<>();
989 containersTable.put(name, idList);
998 return containersTable;
1004 private void clearContainerTable() {
1005 containersTable = null;
List< AbstractFile > findFiles(String fileName)