Skip to content
Snippets Groups Projects
Commit b9e74dbc authored by blackoverlord's avatar blackoverlord
Browse files

refactoring

- added more accessors to ShellLink
- moved creation stuff to ShellLinkHelper
- improved support for non windows environment
parent c732a845
No related branches found
No related tags found
No related merge requests found
......@@ -18,6 +18,7 @@ import io.ByteReader;
import io.ByteWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
......@@ -199,7 +200,7 @@ public class LinkInfo implements Serializable {
localBasePath = s;
if (vid == null) vid = new VolumeID();
lif.setVolumeIDAndLocalBasePath();
lif.setVolumeIDAndLocalBasePath();
return this;
}
......@@ -226,4 +227,21 @@ public class LinkInfo implements Serializable {
lif.setCommonNetworkRelativeLinkAndPathSuffix();
return this;
}
public String buildPath() {
if (localBasePath != null) {
String path = localBasePath;
if (commonPathSuffix != null && !commonPathSuffix.equals("")) {
if (path.charAt(path.length() - 1) != File.separatorChar)
path += File.separatorChar;
path += commonPathSuffix;
}
return path;
}
if (cnrlink != null && commonPathSuffix != null)
return cnrlink.getNetName() + "\\" + commonPathSuffix;
return null;
}
}
......@@ -18,7 +18,6 @@ import io.ByteReader;
import io.ByteWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
......@@ -92,7 +91,7 @@ public class LinkTargetIDList extends LinkedList<ItemID> implements Serializable
if (i.getType() == ItemID.TYPE_DRIVE || i.getType() == ItemID.TYPE_DRIVE_OLD)
path.append(i.getName());
else if (i.getType() == ItemID.TYPE_DIRECTORY || i.getType() == ItemID.TYPE_DIRECTORY_OLD)
path.append(i.getName() + File.separator);
path.append(i.getName() + "\\");
else if (i.getType() == ItemID.TYPE_FILE || i.getType() == ItemID.TYPE_FILE_OLD)
path.append(i.getName());
}
......
......@@ -15,10 +15,14 @@
package mslinks;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import mslinks.ShellLinkHelper.Options;
public class Main {
public static void main(String[] args) throws IOException {
ShellLink sl = ShellLink.createLink("pause.bat")
public static void main(String[] args) throws IOException, ShellLinkException {
var sl = new ShellLink()
.setWorkingDir("..")
.setIconLocation("%SystemRoot%\\system32\\SHELL32.dll");
sl.getHeader().setIconIndex(128);
......@@ -26,8 +30,14 @@ public class Main {
.setFont(mslinks.extra.ConsoleData.Font.Consolas)
.setFontSize(24)
.setTextColor(5);
sl.saveTo("testlink.lnk");
Path targetPath = Paths.get("pause.bat").toAbsolutePath();
String root = targetPath.getRoot().toString();
String pathWithoutRoot = targetPath.subpath(0, targetPath.getNameCount()).toString();
var helper = new ShellLinkHelper(sl)
.setLocalTarget(root, pathWithoutRoot, Options.None)
.saveTo("testlink.lnk");
System.out.println(sl.getWorkingDir());
System.out.println(sl.resolveTarget());
}
......
......@@ -27,12 +27,8 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import mslinks.data.CNRLink;
import mslinks.data.ItemID;
import mslinks.data.LinkFlags;
import mslinks.data.VolumeID;
import mslinks.extra.ConsoleData;
import mslinks.extra.ConsoleFEData;
import mslinks.extra.EnvironmentVariable;
......@@ -43,8 +39,6 @@ import mslinks.extra.VistaIDList;
public class ShellLink {
public static final String VERSION = "1.0.6";
private static Map<String, String> env = System.getenv();
private static HashMap<Integer, Class<? extends Serializable>> extraTypes = new HashMap<>(Map.of(
ConsoleData.signature, ConsoleData.class,
......@@ -126,7 +120,7 @@ public class ShellLink {
}
}
private void serialize(OutputStream out) throws IOException {
public void serialize(OutputStream out) throws IOException {
LinkFlags lf = header.getLinkFlags();
ByteWriter bw = new ByteWriter(out);
header.serialize(bw);
......@@ -161,6 +155,25 @@ public class ShellLink {
header.getLinkFlags().setHasLinkInfo();
return info;
}
public ShellLink removeLinkInfo() {
info = null;
header.getLinkFlags().clearHasLinkInfo();
return this;
}
public LinkTargetIDList getTargetIdList() { return idlist; }
public LinkTargetIDList createTargetIdList() {
if (idlist == null) {
idlist = new LinkTargetIDList();
header.getLinkFlags().setHasLinkTargetIDList();
}
return idlist;
}
public ShellLink removeTargetIdList() {
idlist = null;
header.getLinkFlags().clearHasLinkTargetIDList();
return this;
}
public String getName() { return name; }
public ShellLink setName(String s) {
......@@ -191,7 +204,6 @@ public class ShellLink {
header.getLinkFlags().clearHasWorkingDir();
else {
header.getLinkFlags().setHasWorkingDir();
s = Paths.get(s).toAbsolutePath().normalize().toString();
}
workingDir = s;
return this;
......@@ -213,61 +225,67 @@ public class ShellLink {
header.getLinkFlags().clearHasIconLocation();
else {
header.getLinkFlags().setHasIconLocation();
String t = resolveEnvVariables(s);
if (!Paths.get(t).isAbsolute())
s = Paths.get(s).toAbsolutePath().toString();
}
iconLocation = s;
return this;
}
public ConsoleData getConsoleData() {
ConsoleData cd = (ConsoleData)extra.get(ConsoleData.signature);
if (cd == null) {
cd = new ConsoleData();
extra.put(ConsoleData.signature, cd);
}
return cd;
}
public String getLanguage() {
ConsoleFEData cd = (ConsoleFEData)extra.get(ConsoleFEData.signature);
if (cd == null) {
cd = new ConsoleFEData();
extra.put(ConsoleFEData.signature, cd);
}
return cd.getLanguage();
return getConsoleFEData().getLanguage();
}
public ShellLink setLanguage(String s) {
ConsoleFEData cd = (ConsoleFEData)extra.get(ConsoleFEData.signature);
if (cd == null) {
cd = new ConsoleFEData();
extra.put(ConsoleFEData.signature, cd);
}
cd.setLanguage(s);
getConsoleFEData().setLanguage(s);
return this;
}
public ShellLink saveTo(String path) throws IOException {
linkFileSource = Paths.get(path).toAbsolutePath().normalize();
if (Files.isDirectory(linkFileSource))
throw new IOException("path is directory!");
if (!header.getLinkFlags().hasRelativePath()) {
Path target = Paths.get(resolveTarget());
Path origin = linkFileSource.getParent();
if (target.getRoot().equals(origin.getRoot()))
setRelativePath(origin.relativize(target).toString());
}
if (!header.getLinkFlags().hasWorkingDir()) {
Path target = Paths.get(resolveTarget());
if (!Files.isDirectory(target))
setWorkingDir(target.getParent().toString());
}
serialize(Files.newOutputStream(linkFileSource));
public ConsoleData getConsoleData() {
return (ConsoleData)getExtraDataBlock(ConsoleData.signature);
}
public ShellLink removeConsoleData() {
extra.remove(ConsoleData.signature);
return this;
}
public ConsoleFEData getConsoleFEData() {
return (ConsoleFEData)getExtraDataBlock(ConsoleFEData.signature);
}
public ShellLink removeConsoleFEData() {
extra.remove(ConsoleFEData.signature);
return this;
}
public EnvironmentVariable getEnvironmentVariable() {
return (EnvironmentVariable)getExtraDataBlock(EnvironmentVariable.signature);
}
public ShellLink removeEnvironmentVariable() {
extra.remove(EnvironmentVariable.signature);
return this;
}
public Tracker getTracker() {
return (Tracker)getExtraDataBlock(Tracker.signature);
}
public ShellLink removeTracker() {
extra.remove(Tracker.signature);
return this;
}
public VistaIDList getVistaIDList() {
return (VistaIDList)getExtraDataBlock(VistaIDList.signature);
}
public ShellLink removeVistaIDList() {
extra.remove(VistaIDList.signature);
return this;
}
/**
* linkFileSource is the location where the lnk file is stored
* used only to build relative path and is not serialized
*/
public Path getLinkFileSource() { return linkFileSource; }
public ShellLink setLinkFileSource(Path path) {
linkFileSource = path;
return this;
}
......@@ -277,96 +295,76 @@ public class ShellLink {
}
if (header.getLinkFlags().hasLinkInfo() && info != null) {
CNRLink l = info.getCommonNetworkRelativeLink();
String cps = info.getCommonPathSuffix();
String lbp = info.getLocalBasePath();
if (lbp != null) {
String path = lbp;
if (cps != null && !cps.equals("")) {
if (path.charAt(path.length() - 1) != File.separatorChar)
path += File.separatorChar;
path += cps;
}
String path = info.buildPath();
if (path != null)
return path;
}
if (l != null && cps != null)
return l.getNetName() + File.separator + cps;
}
if (linkFileSource != null && header.getLinkFlags().hasRelativePath() && relativePath != null)
return linkFileSource.resolveSibling(relativePath).normalize().toString();
var envBlock = (EnvironmentVariable)extra.get(EnvironmentVariable.signature);
if (envBlock != null && !envBlock.getVariable().isEmpty())
return envBlock.getVariable();
return "<unknown>";
}
private Serializable getExtraDataBlock(int signature) {
Serializable block = extra.get(signature);
if (block == null) {
Class<?> type = extraTypes.get(signature);
try {
block = (Serializable)type.getConstructor().newInstance();
extra.put(signature, block);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
return block;
}
@Deprecated(since = "1.0.7", forRemoval = true)
public ShellLink saveTo(String path) throws IOException {
new ShellLinkHelper(this).saveTo(path);
return this;
}
/**
* Set path of target file of directory. Function accepts local paths and network paths.
* Environment variables are accepted but resolved here and aren't kept in link.
*/
@Deprecated(since = "1.0.7", forRemoval = true)
public ShellLink setTarget(String target) {
target = resolveEnvVariables(target);
Path tar = Paths.get(target).toAbsolutePath();
target = tar.toString();
if (target.startsWith("\\\\")) {
int p1 = target.indexOf('\\', 2);
int p2 = target.indexOf('\\', p1+1);
LinkInfo inf = createLinkInfo();
inf.createCommonNetworkRelativeLink().setNetName(target.substring(0, p2));
inf.setCommonPathSuffix(target.substring(p2+1));
if (Files.isDirectory(Paths.get(target)))
header.getFileAttributesFlags().setDirecory();
header.getLinkFlags().setHasExpString();
extra.put(EnvironmentVariable.signature, new EnvironmentVariable().setVariable(target));
} else try {
header.getLinkFlags().setHasLinkTargetIDList();
idlist = new LinkTargetIDList();
String[] path = target.split("\\\\");
idlist.add(new ItemID().setType(ItemID.TYPE_CLSID));
idlist.add(new ItemID().setType(ItemID.TYPE_DRIVE).setName(path[0]));
for (int i=1; i<path.length; i++)
idlist.add(new ItemID().setType(ItemID.TYPE_DIRECTORY).setName(path[i]));
LinkInfo inf = createLinkInfo();
inf.createVolumeID().setDriveType(VolumeID.DRIVE_FIXED);
inf.setLocalBasePath(target);
if (Files.isDirectory(tar))
header.getFileAttributesFlags().setDirecory();
else
idlist.getLast().setType(ItemID.TYPE_FILE);
target = ShellLinkHelper.resolveEnvVariables(target);
String targetAbsPath = Paths.get(target).toAbsolutePath().toString();
try {
var helper = new ShellLinkHelper(new ShellLink());
if (targetAbsPath.startsWith("\\\\")) {
helper.setNetworkTarget(targetAbsPath);
} else {
String[] parts = targetAbsPath.split(":");
if (parts.length == 2)
helper.setLocalTarget(parts[0], parts[1]);
}
} catch (ShellLinkException e) {}
return this;
}
@Deprecated(since = "1.0.7", forRemoval = true)
public static ShellLink createLink(String target) {
ShellLink sl = new ShellLink();
sl.setTarget( target );
return sl;
}
/**
* equivalent to createLink(target).saveTo(linkpath)
*/
@Deprecated(since = "1.0.7", forRemoval = true)
public static ShellLink createLink(String target, String linkpath) throws IOException {
return createLink(target).saveTo(linkpath);
}
private static String resolveEnvVariables(String path) {
for (var i : env.entrySet()) {
String p = Pattern.quote(i.getKey());
String r = i.getValue().replace("\\", "\\\\");
path = Pattern.compile("%"+p+"%", Pattern.CASE_INSENSITIVE).matcher(path).replaceAll(r);
}
return path;
}
}
/*
https://github.com/BlackOverlord666/mslinks
Copyright (c) 2015 Dmitrii Shamrikov
Licensed under the WTFPL
You may obtain a copy of the License at
http://www.wtfpl.net/about/
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package mslinks;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.regex.Pattern;
import mslinks.data.ItemID;
import mslinks.data.VolumeID;
/**
* Helper class to manipulate ShellLink properties in batches for common tasks
* ShellLink can be used directly without helper for more detailed set up
*/
public class ShellLinkHelper {
public enum Options {
None,
ForceTypeDirectory,
ForceTypeFile,
}
protected ShellLink link;
public ShellLinkHelper(ShellLink l) {
link = l;
}
public ShellLink getLink() { return link; }
/**
* Sets LAN target path
* @param path is an absolute in the form '\\host\share\path\to\target'
* @throws ShellLinkException
*/
public ShellLinkHelper setNetworkTarget(String path) throws ShellLinkException {
return setNetworkTarget(path, Options.None);
}
/**
* Sets LAN target path
* @param path is an absolute in the form '\\host\share\path\to\target'
* @throws ShellLinkException
*/
public ShellLinkHelper setNetworkTarget(String path, Options options) throws ShellLinkException {
if (!path.startsWith("\\"))
path = "\\" + path;
if (!path.startsWith("\\\\"))
path = "\\" + path;
int p1 = path.indexOf('\\', 2); // hostname
int p2 = path.indexOf('\\', p1+1); // share name
if (p1 != -1) {
LinkInfo info = link.getHeader().getLinkFlags().hasLinkInfo() ? link.getLinkInfo() : link.createLinkInfo();
if (p2 != -1) {
info.createCommonNetworkRelativeLink().setNetName(path.substring(0, p2));
info.setCommonPathSuffix(path.substring(p2+1));
} else {
info.createCommonNetworkRelativeLink().setNetName(path);
info.setCommonPathSuffix("");
}
link.getHeader().getFileAttributesFlags().setDirecory();
boolean forceFile = options == Options.ForceTypeFile;
boolean forceDirectory = options == Options.ForceTypeDirectory;
if (forceFile || !forceDirectory && Files.isRegularFile(Paths.get(path))) {
link.getHeader().getFileAttributesFlags().clearDirecory();
}
} else {
link.getHeader().getFileAttributesFlags().clearDirecory();
}
link.getHeader().getLinkFlags().setHasExpString();
link.getEnvironmentVariable().setVariable(path);
return this;
}
/**
* Sets target on local computer, e.g. "C:\path\to\target"
* @param drive is a letter part of the path, e.g. "C" or "D"
* @param absolutePath is a path in the specified drive, e.g. "path\to\target"
* @return
* @throws ShellLinkException
*/
public ShellLinkHelper setLocalTarget(String drive, String absolutePath) throws ShellLinkException {
return setLocalTarget(drive, absolutePath, Options.None);
}
/**
* Sets target on local computer, e.g. "C:\path\to\target"
* @param drive is a letter part of the path, e.g. "C" or "D"
* @param absolutePath is a path in the specified drive, e.g. "path\to\target"
* @return
* @throws ShellLinkException
*/
public ShellLinkHelper setLocalTarget(String drive, String absolutePath, Options options) throws ShellLinkException {
link.getHeader().getLinkFlags().setHasLinkTargetIDList();
var idList = link.createTargetIdList();
idList.add(new ItemID().setType(ItemID.TYPE_CLSID)); // this computer - the only supported class id so far
idList.add(new ItemID().setType(ItemID.TYPE_DRIVE).setName(drive));
absolutePath = absolutePath.replaceAll("^(\\\\|\\/)", "");
String absoluteTargetPath = idList.get(1).getName() + absolutePath;
String[] path = absolutePath.split("\\\\|\\/");
for (String i : path)
idList.add(new ItemID().setType(ItemID.TYPE_DIRECTORY).setName(i));
LinkInfo info = link.getHeader().getLinkFlags().hasLinkInfo() ? link.getLinkInfo() : link.createLinkInfo();
info.createVolumeID().setDriveType(VolumeID.DRIVE_FIXED);
info.setLocalBasePath(absoluteTargetPath);
link.getHeader().getFileAttributesFlags().setDirecory();
boolean forceFile = options == Options.ForceTypeFile;
boolean forceDirectory = options == Options.ForceTypeDirectory;
if (forceFile || !forceDirectory && Files.isRegularFile(Paths.get(absoluteTargetPath))) {
link.getHeader().getFileAttributesFlags().clearDirecory();
idList.getLast().setType(ItemID.TYPE_FILE);
}
return this;
}
/**
* Serializes {@code ShellLink} to specified {@code path}. Sets appropriate relative path
* and working directory if possible and if they are not already set
*/
public ShellLinkHelper saveTo(String path) throws IOException {
Path savingPath = Paths.get(path).toAbsolutePath().normalize();
if (Files.isDirectory(savingPath))
throw new IOException("can't save ShellLink to \"" + savingPath + "\" because there is a directory with this name");
link.setLinkFileSource(savingPath);
Path target = Paths.get(link.resolveTarget());
if (!link.getHeader().getLinkFlags().hasRelativePath()) {
Path savingDir = savingPath.getParent();
// this will always be false on linux
if (savingDir.getRoot().equals(target.getRoot()))
link.setRelativePath(savingDir.relativize(target).toString());
}
if (!link.getHeader().getLinkFlags().hasWorkingDir()) {
// this will always be false on linux
if (Files.isRegularFile(target))
link.setWorkingDir(target.getParent().toString());
}
link.serialize(Files.newOutputStream(savingPath));
return this;
}
/**
* Universal all-by-default creation of the link
* @param target - absolute path for the target file in windows format (e.g. C:\path\to\file.txt)
* @param linkpath - where to save link file
* @return
* @throws IOException
* @throws ShellLinkException
*/
public static ShellLinkHelper createLink(String target, String linkpath) throws IOException, ShellLinkException {
target = resolveEnvVariables(target);
var helper = new ShellLinkHelper(new ShellLink());
if (target.startsWith("\\\\")) {
helper.setNetworkTarget(target);
} else {
String[] parts = target.split(":");
if (parts.length != 2)
throw new ShellLinkException("Wrong path '" + target + "'");
helper.setLocalTarget(parts[0], parts[1]);
}
helper.saveTo(linkpath);
return helper;
}
public static String resolveEnvVariables(String path) {
for (var i : env.entrySet()) {
String p = Pattern.quote(i.getKey());
String r = i.getValue().replace("\\", "\\\\");
path = Pattern.compile("%"+p+"%", Pattern.CASE_INSENSITIVE).matcher(path).replaceAll(r);
}
return path;
}
private static Map<String, String> env = System.getenv();
}
......@@ -28,6 +28,9 @@ public class VistaIDList implements Serializable {
public static final int signature = 0xA000000C;
private LinkedList<byte[]> list = new LinkedList<>();
public VistaIDList() {
}
public VistaIDList(ByteReader br, int size) throws ShellLinkException, IOException {
if (size < 0xa)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment