From 56fec05d450efbb8044f4e7113bbd3dafdd644c4 Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Fri, 23 Aug 2024 11:48:57 -0400 Subject: [PATCH 1/5] '#2307 Puts LNKShortcutParser in second queue as it must find the referenced item to make the reference. --- .../src/main/java/iped/engine/core/QueuesProcessingOrder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iped-engine/src/main/java/iped/engine/core/QueuesProcessingOrder.java b/iped-engine/src/main/java/iped/engine/core/QueuesProcessingOrder.java index d31dd34b7d..9884b02e41 100644 --- a/iped-engine/src/main/java/iped/engine/core/QueuesProcessingOrder.java +++ b/iped-engine/src/main/java/iped/engine/core/QueuesProcessingOrder.java @@ -17,6 +17,7 @@ import iped.parsers.discord.DiscordParser; import iped.parsers.emule.KnownMetParser; import iped.parsers.emule.PartMetParser; +import iped.parsers.lnk.LNKShortcutParser; import iped.parsers.mail.RFC822Parser; import iped.parsers.mail.win10.Win10MailParser; import iped.parsers.python.PythonParser; @@ -62,6 +63,7 @@ private static Map installTypesToPostProcess() { // handle wal logs mediaTypes.put(SQLite3Parser.MEDIA_TYPE, 2); + mediaTypes.put(MediaType.parse(LNKShortcutParser.LNK_MIME_TYPE), 2); // must be after sqlite processing to find storage_db.db mediaTypes.put(SkypeParser.SKYPE_MIME, 3); From b5df46ecbd40be8a44a3e6deafde7eb27f834958 Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Fri, 23 Aug 2024 11:51:09 -0400 Subject: [PATCH 2/5] '#2307 Creates propery to represent and save mft idx, so it can be used to search for items in the case later. --- .../src/main/java/iped/parsers/lnk/LNKParser.java | 1 + .../java/iped/parsers/lnk/LNKShellItemFileEntry.java | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKParser.java index 04a03945a6..58bdaa21e7 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKParser.java @@ -640,6 +640,7 @@ public static void parseExtensionBlock(byte[] b, int posSig, LNKShellItem objIte long indMft = toInt6(b, posTmp); int seqNum = toSmall(b, posTmp + 6); if (indMft > 0) { + fEntry.setIndMft(indMft); fEntry.setNtfsRef( "MFT Entry Idx " + String.valueOf(indMft) + " - Seq.Numb. " + String.valueOf(seqNum)); //$NON-NLS-1$ //$NON-NLS-2$ } diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShellItemFileEntry.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShellItemFileEntry.java index 5d85e5ed41..f55c63c1b1 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShellItemFileEntry.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShellItemFileEntry.java @@ -9,6 +9,7 @@ public class LNKShellItemFileEntry { private long fileSize, modifiedDate, createDate, accessDate; private int fileAttributeFlags, tipoShell; private String primaryName, secondaryName, guidShellFolder, ntfsRef, unknown; + private long indMft; private StringBuffer extensionSigs, localizedNames; public int getTipoShell() { @@ -175,4 +176,12 @@ public static String toDateStrFromFAT(DateFormat df, long ft) { return df.format(calendar.getTime()); } + + public long getIndMft() { + return indMft; + } + + public void setIndMft(long indMft) { + this.indMft = indMft; + } } From 3efcc5ff0bb9844f700d8d90395d1da017ce39a0 Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Fri, 23 Aug 2024 11:53:28 -0400 Subject: [PATCH 3/5] '#2307 Adds some info as Metadata to each LNK file parsed, and makes the reference to any correspondent found item in case. The search is first based on metaAddress (mft idx) and latter, if not successfull, on relative path, after removing any volume letter info. The creationDate info is compared to confirm the match. --- .../iped/parsers/lnk/LNKShortcutParser.java | 144 +++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java index e61bddc99d..10951c4abf 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java @@ -23,6 +23,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.TimeZone; @@ -38,7 +39,11 @@ import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; +import iped.data.IItemReader; import iped.parsers.util.Messages; +import iped.properties.BasicProps; +import iped.properties.ExtraProperties; +import iped.search.IItemSearcher; /** * Parser para arquivos de atalho (LNK) do Windows Referencias utilizadas sobre @@ -56,6 +61,15 @@ public class LNKShortcutParser extends AbstractParser { private static final Set SUPPORTED_TYPES = Collections.singleton(MediaType.application("x-lnk")); //$NON-NLS-1$ public static final String LNK_MIME_TYPE = "application/x-lnk"; //$NON-NLS-1$ + public static final String LNK_METADATA_PREFIX = "lnk"; + public static final String LNK_METADATA_LOCALPATHINFO = "localPathInfo"; + public static final String LNK_METADATA_LOCALPATH = "localPath"; + public static final String LNK_METADATA_COMMONPATH = "commonPath"; + public static final String LNK_METADATA_NETWORKSHARE = "networkShare"; + public static final String LNK_METADATA_VOLUMELABEL = "volumeLabel"; + public static final String LNK_METADATA_FILEEXISTS = "fileExists"; + public static final String LNK_METADATA_FILEMODIFIED = "modifiedAfterOpen"; + @Override public Set getSupportedTypes(ParseContext arg0) { return SUPPORTED_TYPES; @@ -95,9 +109,45 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, showHeader(lnkObj, df, xhtml); // HasLinkInfo - if (lnkObj.hasLinkLocation()) + if (lnkObj.hasLinkLocation()) { showLinkLocation(lnkObj, df, xhtml); + // According to + // https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc#4-location-information + // the real local path is the concatenation of netshare, commonPath and + // localPath + String fullLocalPath = ""; + LNKLinkLocation lnkLoc = lnkObj.getLinkLocation(); + + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_LOCALPATHINFO, lnkLoc.getLocalPath()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_COMMONPATH, lnkLoc.getCommonPath()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_NETWORKSHARE, lnkLoc.getNetShare()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_VOLUMELABEL, lnkLoc.getVolumeLabel()); + + if (lnkLoc.getNetShare() != null) { + fullLocalPath = fullLocalPath + lnkLoc.getNetShare(); + if (!fullLocalPath.endsWith("\\")) { + fullLocalPath += "\\"; + } + } + if (lnkLoc.getCommonPath() != null) { + fullLocalPath = fullLocalPath + lnkLoc.getCommonPath(); + if (!fullLocalPath.equals("") && !fullLocalPath.endsWith("\\")) { + fullLocalPath += "\\"; + } + } + fullLocalPath = fullLocalPath + lnkLoc.getLocalPath(); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_LOCALPATH, fullLocalPath); + + try { + makeReference(metadata, context, lnkObj, fullLocalPath, df); + } catch (Exception e) { + // unpredictable error when making reference + e.printStackTrace(); + } + + } + // HasName HasRelativePath HasWorkingDir HasArguments HasIconLocation if (lnkObj.hasName() || lnkObj.hasRelativePath() || lnkObj.hasWorkingDir() || lnkObj.hasArguments() || lnkObj.hasIconLocation()) @@ -118,6 +168,98 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, xhtml.endDocument(); } + // + // Makes the reference to a found target if: + // 1) the metaAddress (mft idx in NTFS) and creation date is the same. + // - Only metaAddress can reference different file in other filesystem, as the + // number can in different FS may coincide. So creationDate is used to confirm. + // 2) the path is similar and creation date is the same + // - CreationDate is considerably reliable value, assuming no dates + // manipulation, as it is not probable 2 files created at same time. The path is + // used to confirm. + // + // If creation date is different, it still can mean that the file was moved to + // a different partition or drive (different file system). + // So here comes the question: should it still be considered the same file if + // only size and name is the same? Meanwhile, in this first version, it isn't. + private void makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, String fullLocalPath, DateFormat df) { + if (fullLocalPath.startsWith("file://")) { + fullLocalPath.substring(7); + } + LNKLinkLocation lnkLoc = lnkObj.getLinkLocation(); + if (lnkLoc.getNetShare() == null) { + // tries to link to local file only if net info not defined + IItemSearcher searcher = context.get(IItemSearcher.class); + if (searcher != null) { + + List items = null; + boolean mftIdxFound = false; + + if (lnkObj.hasTargetIDList() && lnkObj.getShellTargetIDList().size() > 0) { + // search based on MFT entry index, if existent + LNKShellItem lastTarget = lnkObj.getShellTargetIDList().get(lnkObj.getShellTargetIDList().size() - 1); + if (lastTarget.hasFileEntry()) { + LNKShellItemFileEntry fEntry = lastTarget.getFileEntry(); + items = searcher.search(BasicProps.META_ADDRESS + ":" + fEntry.getIndMft()); + if (items.size() <= 0) { + items = null; + } else { + mftIdxFound = true; + } + } + } + + if (items == null || makeReference(metadata, context, lnkObj, items, df) == null) {// if no reference could be done based on metaAddress + // searches based on path + String relLocalPath = fullLocalPath.replace("\\", "\\\\"); + int i = relLocalPath.indexOf(":");// search for drive letter separator + if (i > 0) { + relLocalPath = relLocalPath.substring(i + 1);// gets path starting from drive path separator + } + items = searcher.search(BasicProps.PATH + ":\"" + relLocalPath + "\""); + + makeReference(metadata, context, lnkObj, items, df); + } + } + } + } + + private IItemReader makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, List items, DateFormat df) { + for (IItemReader iReader : items) { + // creation date will confirm that the item is from the correct volume/path + Date created = iReader.getCreationDate(); + if (created != null) { + if (df.format(created).equals(lnkObj.getCreateDate(df))) { + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEEXISTS, "true"); + metadata.add(ExtraProperties.LINKED_ITEMS, BasicProps.ID + ":" + iReader.getId()); + + // if item with same path exists, mark it. + boolean sizeMatches = false; + if (iReader.getLength() == lnkObj.getFileSize()) { + sizeMatches = true; + } + + Date modifiedDate = iReader.getModDate(); + if (modifiedDate != null) { + if (!df.format(modifiedDate).equals(lnkObj.getModifiedDate(df))) { + // if item moddate is different than the registered in LNK file, informs that it + // was modified after seen by this link + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEMODIFIED, "true"); + } + } + if (!sizeMatches) { + // if item size is different than the registered in LNK file, informs that it + // was modified after seen by this link + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEMODIFIED, "true"); + } + return iReader; + } + } + } + return null; + } + + private void showHeader(LNKShortcut lnkObj, DateFormat df, XHTMLContentHandler xhtml) throws Exception { xhtml.startElement("table", "class", "t"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ addRowHeader(xhtml, Messages.getString("LNKShortcutParser.FileHeader")); //$NON-NLS-1$ From 6d7439ba1eb9a7f8554cfa97e527d7513e0702de Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Mon, 26 Aug 2024 10:01:18 -0400 Subject: [PATCH 4/5] '#2308 Initial code to extract origin timestamps also. --- .../iped/parsers/lnk/LNKShortcutParser.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java index 10951c4abf..99f22bc51c 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Set; import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.HttpHeaders; @@ -41,6 +42,7 @@ import iped.data.IItemReader; import iped.parsers.util.Messages; +import iped.parsers.util.MetadataUtil; import iped.properties.BasicProps; import iped.properties.ExtraProperties; import iped.search.IItemSearcher; @@ -70,18 +72,30 @@ public class LNKShortcutParser extends AbstractParser { public static final String LNK_METADATA_FILEEXISTS = "fileExists"; public static final String LNK_METADATA_FILEMODIFIED = "modifiedAfterOpen"; + static AtomicBoolean initialized = new AtomicBoolean(false); + @Override public Set getSupportedTypes(ParseContext arg0) { return SUPPORTED_TYPES; + } @Override public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException { + synchronized (initialized) { + if (initialized.get()) { + MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.CREATED, Date.class); + MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, Date.class); + MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, Date.class); + } + } final DateFormat df = new SimpleDateFormat(Messages.getString("LNKShortcutParser.DateFormat")); //$NON-NLS-1$ df.setTimeZone(TimeZone.getTimeZone("GMT+0")); //$NON-NLS-1$ + final DateFormat dfMetadata = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + metadata.set(HttpHeaders.CONTENT_TYPE, LNK_MIME_TYPE); metadata.remove(TikaCoreProperties.RESOURCE_NAME_KEY); @@ -124,6 +138,10 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_NETWORKSHARE, lnkLoc.getNetShare()); metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_VOLUMELABEL, lnkLoc.getVolumeLabel()); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.CREATED, lnkObj.getCreateDate(dfMetadata)); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, lnkObj.getModifiedDate(dfMetadata)); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, lnkObj.getAccessDate(dfMetadata)); + if (lnkLoc.getNetShare() != null) { fullLocalPath = fullLocalPath + lnkLoc.getNetShare(); if (!fullLocalPath.endsWith("\\")) { From 156b893325b12264919ce7482757e77c58d7b761 Mon Sep 17 00:00:00 2001 From: "patrick.pdb" Date: Mon, 26 Aug 2024 16:43:34 -0400 Subject: [PATCH 5/5] '#2308 Extracts source timestamps registered inside LNK as metadata info. --- .../iped/parsers/lnk/LNKShortcutParser.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java index 99f22bc51c..69f3aa1d63 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/lnk/LNKShortcutParser.java @@ -84,10 +84,11 @@ public Set getSupportedTypes(ParseContext arg0) { public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException { synchronized (initialized) { - if (initialized.get()) { + if (!initialized.get()) { MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.CREATED, Date.class); MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, Date.class); MetadataUtil.setMetadataType(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, Date.class); + initialized.set(true); } } @@ -138,10 +139,6 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_NETWORKSHARE, lnkLoc.getNetShare()); metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_VOLUMELABEL, lnkLoc.getVolumeLabel()); - metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.CREATED, lnkObj.getCreateDate(dfMetadata)); - metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, lnkObj.getModifiedDate(dfMetadata)); - metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, lnkObj.getAccessDate(dfMetadata)); - if (lnkLoc.getNetShare() != null) { fullLocalPath = fullLocalPath + lnkLoc.getNetShare(); if (!fullLocalPath.endsWith("\\")) { @@ -156,9 +153,16 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, } fullLocalPath = fullLocalPath + lnkLoc.getLocalPath(); metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_LOCALPATH, fullLocalPath); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.LENGTH, Long.toString(lnkObj.getFileSize())); try { - makeReference(metadata, context, lnkObj, fullLocalPath, df); + IItemReader refItem = makeReference(metadata, context, lnkObj, fullLocalPath, df, dfMetadata); + if (refItem == null) { + // if, and only if, the source wasn't found, register the timestamps + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.CREATED, lnkObj.getCreateDate(dfMetadata)); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, lnkObj.getModifiedDate(dfMetadata)); + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, lnkObj.getAccessDate(dfMetadata)); + } } catch (Exception e) { // unpredictable error when making reference e.printStackTrace(); @@ -200,10 +204,11 @@ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, // a different partition or drive (different file system). // So here comes the question: should it still be considered the same file if // only size and name is the same? Meanwhile, in this first version, it isn't. - private void makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, String fullLocalPath, DateFormat df) { + private IItemReader makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, String fullLocalPath, DateFormat df, DateFormat dfMetadata) { if (fullLocalPath.startsWith("file://")) { fullLocalPath.substring(7); } + IItemReader result = null; LNKLinkLocation lnkLoc = lnkObj.getLinkLocation(); if (lnkLoc.getNetShare() == null) { // tries to link to local file only if net info not defined @@ -227,7 +232,11 @@ private void makeReference(Metadata metadata, ParseContext context, LNKShortcut } } - if (items == null || makeReference(metadata, context, lnkObj, items, df) == null) {// if no reference could be done based on metaAddress + if (items != null) { + result = makeReference(metadata, context, lnkObj, items, df, dfMetadata); + } + + if (result == null) {// if no reference could be done based on metaAddress // searches based on path String relLocalPath = fullLocalPath.replace("\\", "\\\\"); int i = relLocalPath.indexOf(":");// search for drive letter separator @@ -236,13 +245,16 @@ private void makeReference(Metadata metadata, ParseContext context, LNKShortcut } items = searcher.search(BasicProps.PATH + ":\"" + relLocalPath + "\""); - makeReference(metadata, context, lnkObj, items, df); + if (items != null && items.size() > 0) { + result = makeReference(metadata, context, lnkObj, items, df, dfMetadata); + } } } } + return result; } - private IItemReader makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, List items, DateFormat df) { + private IItemReader makeReference(Metadata metadata, ParseContext context, LNKShortcut lnkObj, List items, DateFormat df, DateFormat dfMetadata) { for (IItemReader iReader : items) { // creation date will confirm that the item is from the correct volume/path Date created = iReader.getCreationDate(); @@ -263,6 +275,19 @@ private IItemReader makeReference(Metadata metadata, ParseContext context, LNKSh // if item moddate is different than the registered in LNK file, informs that it // was modified after seen by this link metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + LNK_METADATA_FILEMODIFIED, "true"); + + // if LNK registered source last modified date is different from found source + // last modified date, adds the metadata + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.MODIFIED, lnkObj.getModifiedDate(dfMetadata)); + } + } + + Date acessedDate = iReader.getAccessDate(); + if (acessedDate != null) { + // if LNK registered source access date is different from found source access + // date, adds the metadata + if (!df.format(acessedDate).equals(lnkObj.getModifiedDate(df))) { + metadata.add(LNK_METADATA_PREFIX + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER + BasicProps.ACCESSED, lnkObj.getAccessDate(dfMetadata)); } } if (!sizeMatches) {