diff --git a/ant/apple/apple-launcher.sh.in b/ant/apple/apple-launcher.sh.in
index 6e8e8eacb..0efb362f8 100644
--- a/ant/apple/apple-launcher.sh.in
+++ b/ant/apple/apple-launcher.sh.in
@@ -20,6 +20,12 @@ iconpath=$installpath/${apple.resources}/${apple.icon}
${apple.jvmver} > /dev/null 2>&1
fallback=$?
+# If launched at startup, check override first
+if [[ "$1" == "--autostart" ]] && [[ "$(cat "$installpath/.autostart")" == "0" ]]; then
+ echo "Skipping autostart"
+ exit 0
+fi
+
# Fallback on Internet Plug-Ins version if needed
if [ $fallback -eq 0 ]; then
${apple.jvmcmd} java ${launch.opts} -Xdock:name="${project.name}" -Xdock:icon="$iconpath" -jar "$jarpath"
diff --git a/ant/apple/apple-postinstall.sh.in b/ant/apple/apple-postinstall.sh.in
index f1158fbb5..350c90c6a 100644
--- a/ant/apple/apple-postinstall.sh.in
+++ b/ant/apple/apple-postinstall.sh.in
@@ -20,6 +20,33 @@ if [ $? -eq 0 ]; then
"${apple.installdir}/auth/firefox/${firefoxcert.name}" "install"
fi
+# Install startup
+site=$(echo "${vendor.website}"|rev|cut -d/ -f1|rev)
+package=$(echo "$site" |rev |cut -d. -f1|rev).$(echo "$site" |rev |cut -d. -f2|rev).${project.filename}
+cat > /Library/LaunchAgents/$package.plist << EOT
+
+
+
+
+ Label$package
+ KeepAlive
+
+ SuccessfulExit
+ AfterInitialDemand
+
+ RunAtLoad
+ ProgramArguments
+
+ ${apple.installdir}/${apple.macos}/${project.name}
+ --autostart
+
+
+
+EOT
+
+printf 1 > "${apple.installdir}/.autostart"
+chmod 777 "${apple.installdir}/.autostart"
+
# Cleanup resources from previous versions
rm -rf "${apple.installdir}/demo/js/3rdparty"
rm "${apple.installdir}/demo/js/qz-websocket.js"
diff --git a/ant/apple/apple-uninstall.sh.in b/ant/apple/apple-uninstall.sh.in
index 91142fda6..30b9fc7f2 100644
--- a/ant/apple/apple-uninstall.sh.in
+++ b/ant/apple/apple-uninstall.sh.in
@@ -26,6 +26,11 @@ else
echo -e "${bash.skipped}"
fi
+# Remove startup entry
+site=$(echo "${vendor.website}"|rev|cut -d/ -f1|rev)
+package=$(echo "$site" |rev |cut -d. -f1|rev).$(echo "$site" |rev |cut -d. -f2|rev).${project.filename}
+rm -f /Library/LaunchAgents/$package.plist
+
# Uninstall ${project.name} system certificates
"${apple.installdir}/auth/${apple.keygen.name}" "uninstall"
diff --git a/ant/firefox/locator.sh.in b/ant/firefox/locator.sh.in
index df332aa13..998ec9c24 100755
--- a/ant/firefox/locator.sh.in
+++ b/ant/firefox/locator.sh.in
@@ -64,7 +64,7 @@ function get_targetdir()
lowerbin=$(echo "$with_bin" |tr '[:upper:]' '[:lower:]')
location=$(readlink -f "$(which $lowerdir)")
targetdir=$(dirname "$location")
- if [ -f "$targetdir/$lowerbin" ]; then
+ if [[ "$targetdir" != "/usr/bin" ]] && [ -f "$targetdir/$lowerbin" ]; then
dir_out="$targetdir"
return 0
else
diff --git a/ant/linux/linux-installer.sh.in b/ant/linux/linux-installer.sh.in
index 3c064b1fd..0c78818f8 100644
--- a/ant/linux/linux-installer.sh.in
+++ b/ant/linux/linux-installer.sh.in
@@ -192,6 +192,24 @@ progress_dialog 92 "Installing usb/udev rules..."
rm -f "/lib/udev/rules.d/${linux.udev.name}" > /dev/null 2>&1
mv "$destdir/${linux.udev.name}" "/lib/udev/rules.d/${linux.udev.name}" > /dev/null 2>&1
+progress_dialog 93 "Cleaning up old versions..."
+
+# Remove old startup entries
+for i in /home/* ; do
+ if [ -n "${project.filename}" ]; then
+ rm "$i/.config/autostart/${project.filename}.desktop" > /dev/null 2>&1
+ fi
+ if [ -n "${project.name}" ]; then
+ rm "$i/.config/autostart/${project.name}.desktop" > /dev/null 2>&1
+ fi
+done
+
+progress_dialog 94 "Adding startup entry..."
+cp "${shortcut}" "/etc/xdg/autostart/${project.filename}.desktop"
+
+# Allow oridinary users to write
+chmod 777 "/etc/xdg/autostart/${project.filename}.desktop"
+
cd "${destdir}"
progress_dialog 95 "Installation complete... Starting ${project.name}..."
which sudo > /dev/null 1>&2
diff --git a/ant/linux/linux-keygen.sh.in b/ant/linux/linux-keygen.sh.in
index cf4f173a8..2d3a482a3 100644
--- a/ant/linux/linux-keygen.sh.in
+++ b/ant/linux/linux-keygen.sh.in
@@ -145,6 +145,12 @@ if [ -n "$sslcertpath" ]; then
exit $status
fi
+# Check for keytool command
+"${jks.keytool} -help" > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+ export PATH=$PATH:/usr/java/latest/bin/
+fi
+
echo "Creating keystore for secure websockets..."
eval "$makekeystore" > /dev/null 2>&1
check_exists "$keystorepath"
diff --git a/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll b/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll
new file mode 100644
index 000000000..0de0324fc
Binary files /dev/null and b/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll differ
diff --git a/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll b/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll
new file mode 100644
index 000000000..cc27f63f4
Binary files /dev/null and b/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll differ
diff --git a/ant/windows/windows-cleanup.js b/ant/windows/windows-cleanup.js
new file mode 100644
index 000000000..7a32778d9
--- /dev/null
+++ b/ant/windows/windows-cleanup.js
@@ -0,0 +1,48 @@
+/** Post-Install Cleanup **/
+
+var app = getArg(0);
+
+if (app) {
+ removeStartupEntries(app);
+} else {
+ WScript.Echo("No app name provided. Exiting.");
+ WScript.Quit(1);
+}
+
+WScript.Quit(0);
+
+/**
+ * Cycles through all users on system and removes matching startup entries.
+ */
+function removeStartupEntries(app) {
+ WScript.Echo('Removing startup entries for all users matching "' + app + '"');
+ var shell = new ActiveXObject("WScript.shell");
+ // get all users
+ var proc = shell.Exec('reg.exe query HKU');
+ var users = proc.StdOut.ReadAll().split(/[\r\n]+/);
+ for (var i = 0; i < users.length; i++) {
+ try {
+ var key = trim(users[i]) + "\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\" + app;
+ shell.RegDelete(key);
+ WScript.Echo(' - [success] Removed "' + app + '" startup in ' + users[i]);
+ } catch(ignore) {}
+ }
+}
+
+/*
+ * Gets then nth argument passed into this script
+ * Returns defaultVal if argument wasn't found
+ */
+function getArg(index, defaultVal) {
+ if (index >= WScript.Arguments.length || trim(WScript.Arguments(index)) == "") {
+ return defaultVal;
+ }
+ return WScript.Arguments(index);
+}
+
+/*
+ * Functional equivalent of foo.trim()
+ */
+function trim(val) {
+ return val.replace(/^\s+/,'').replace(/\s+$/,'');
+}
diff --git a/ant/windows/windows-packager.nsi.in b/ant/windows/windows-packager.nsi.in
index 222f5401e..8b1d5abb7 100644
--- a/ant/windows/windows-packager.nsi.in
+++ b/ant/windows/windows-packager.nsi.in
@@ -95,7 +95,10 @@ Section
; Remove ${vendor.company} certificates
nsExec::ExecToLog "cscript.exe //NoLogo //E:jscript $\"$INSTDIR\auth\${windows.keygen.name}$\" $\"$INSTDIR$\" uninstall"
-
+
+ ; Perform cleanup operations from previous install
+ nsExec::ExecToLog "cscript.exe //NoLogo //E:jscript $\"$INSTDIR\utils\${windows.cleanup.name}$\" $\"${project.name}$\""
+
keygen:
; Exports a self-signed certificate and properties file
DetailPrint "Generating a unique certificate for HTTPS support..."
@@ -123,6 +126,10 @@ Section
${EndIf}
CreateShortCut "$SMPROGRAMS\${project.name}.lnk" "$INSTDIR\${project.filename}.exe" "" "$INSTDIR\${windows.icon}" 0
+ CreateShortCut "$SMSTARTUP\${project.name}.lnk" "$INSTDIR\${project.filename}.exe" "" "$INSTDIR\${windows.icon}" 0
+
+ ; Grant R+W to Authenticated Users (S-1-5-11)
+ AccessControl::GrantOnFile "$SMSTARTUP\${project.name}.lnk" "(S-1-5-11)" "GenericRead + GenericWrite"
; Delete matching firewall rules
DetailPrint "Removing ${project.name} firewall rules..."
@@ -172,6 +179,7 @@ Section "Uninstall"
Delete "$DESKTOP\${project.name}.url"
Delete "$DESKTOP\${project.name}.lnk"
+ Delete "$SMSTARTUP\${project.name}.lnk"
; Sets the context of shell folders to current user
SetShellVarContext current
diff --git a/ant/windows/windows.properties b/ant/windows/windows.properties
index e47257c39..5ee45145a 100644
--- a/ant/windows/windows.properties
+++ b/ant/windows/windows.properties
@@ -10,6 +10,9 @@ windows.keygen.tool=certutil.exe
windows.nsis.addons=${basedir}/ant/windows/nsis
windows.packager.in=${basedir}/ant/windows/windows-packager.nsi.in
windows.packager.out=${build.dir}/windows-packager.nsi
+windows.cleanup.name=windows-cleanup.js
+windows.cleanup.in=${basedir}/ant/windows/${windows.cleanup.name}
+windows.cleanup.out=${dist.dir}/utils/${windows.cleanup.name}
windows.launcher.in=${basedir}/ant/windows/windows-launcher.nsi.in
windows.launcher.out=${build.dir}/windows-launcher.nsi
diff --git a/build.xml b/build.xml
index 16e96ffd4..b6a0f9af7 100644
--- a/build.xml
+++ b/build.xml
@@ -201,6 +201,8 @@
+
+
diff --git a/src/qz/deploy/DeployUtilities.java b/src/qz/deploy/DeployUtilities.java
index 186c171c5..d3cf93bee 100644
--- a/src/qz/deploy/DeployUtilities.java
+++ b/src/qz/deploy/DeployUtilities.java
@@ -113,7 +113,7 @@ public boolean createShortcut(ToggleType toggleType) {
* @param toggleType Shortcut type, i.e. ToggleType.STARTUP
or ToggleType.DESKTOP
* @return Whether or not the shortcut already exists
*/
- public boolean hasShortcut(ToggleType toggleType) {
+ private boolean hasShortcut(ToggleType toggleType) {
boolean hasShortcut = false;
switch(toggleType) {
case STARTUP:
@@ -165,7 +165,7 @@ public boolean removeShortcut(ToggleType toggleType) {
* @return The calculated working path value, or an empty string if one
* could not be determined
*/
- static String getParentDirectory(String filePath) {
+ private static String getParentDirectory(String filePath) {
// Working path should always default to the JARs parent folder
int lastSlash = filePath.lastIndexOf(File.separator);
return lastSlash < 0? "":filePath.substring(0, lastSlash);
@@ -201,25 +201,6 @@ public static DeployUtilities getSystemShortcutCreator() {
}
}
- /**
- * Creates all appropriate parent folders for the file path specified
- *
- * @param filePath The file in which to create parent directories for
- * @return Whether or not the parent folder creation was successful
- */
- static boolean createParentFolder(String filePath) {
- String parentDirectory = getParentDirectory(filePath);
- File f = new File(parentDirectory);
- try {
- f.mkdirs();
- return f.exists();
- }
- catch(SecurityException e) {
- log.error("Error while creating parent directories for: {}", filePath, e);
- }
- return false;
- }
-
/**
* Returns whether or not a file exists
*
@@ -295,7 +276,8 @@ static boolean writeArrayToFile(String filePath, String[] array) {
* @param filePath The full file path to set the execute flag on
* @return true
if successful, false
otherwise
*/
- static boolean setExecutable(String filePath) {
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private static boolean setExecutable(String filePath) {
if (!SystemUtilities.isWindows()) {
try {
File f = new File(filePath);
@@ -314,7 +296,7 @@ static boolean setExecutable(String filePath) {
/**
* Gets the path to qz-tray.properties
*/
- public static String detectPropertiesPath() {
+ private static String detectPropertiesPath() {
// Use supplied path from IDE or command line
// i.e -DsslPropertiesFile=C:\qz-tray.properties
String override = System.getProperty("sslPropertiesFile");
@@ -326,7 +308,6 @@ public static String detectPropertiesPath() {
String propFile = Constants.PROPS_FILE + ".properties";
// Use relative path based on qz-tray.jar, fix %20
- // TODO: Find a better way to fix the unicode chars?
return fixWhitespaces(getParentDirectory(jarPath) + File.separator + propFile);
}
@@ -356,7 +337,7 @@ public static Properties loadTrayProperties() {
* @return A String value representing the absolute path to the currently running
* jar
*/
- public static String detectJarPath() {
+ private static String detectJarPath() {
try {
return new File(DeployUtilities.class.getProtectionDomain()
.getCodeSource().getLocation().getPath()).getCanonicalPath();
@@ -379,15 +360,6 @@ public String getJarPath() {
return jarPath;
}
- /**
- * Set the jar path for which we will create a shortcut for
- *
- * @param jarPath The full file path of the jar file
- */
- public void setJarPath(String jarPath) {
- this.jarPath = jarPath;
- }
-
/**
* Small Enum for differentiating "desktop" and "startup"
*/
diff --git a/src/qz/deploy/LinuxDeploy.java b/src/qz/deploy/LinuxDeploy.java
index cb72b559f..7469354da 100644
--- a/src/qz/deploy/LinuxDeploy.java
+++ b/src/qz/deploy/LinuxDeploy.java
@@ -10,94 +10,99 @@
package qz.deploy;
+import java.awt.Toolkit;
+import java.lang.reflect.Field;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qz.common.Constants;
-import qz.utils.ShellUtilities;
-
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
+import qz.utils.*;
/**
* @author Tres Finocchiaro
*/
-public class LinuxDeploy extends DeployUtilities {
+class LinuxDeploy extends DeployUtilities {
private static final Logger log = LoggerFactory.getLogger(LinuxDeploy.class);
+ private static String STARTUP = "/etc/xdg/autostart/";
+ private static String DESKTOP = System.getProperty("user.home") + "/Desktop/";
+ private static String DELETED_ICON = "process-stop";
+
+ private String appLauncher = "/usr/share/applications/" + getShortcutName();
+
@Override
public boolean createStartupShortcut() {
- return createShortcut(System.getProperty("user.home") + "/.config/autostart/");
+ return copyShortcut(appLauncher, STARTUP);
}
@Override
public boolean createDesktopShortcut() {
- return createShortcut(System.getProperty("user.home") + "/Desktop/");
+ return copyShortcut(appLauncher, DESKTOP);
}
@Override
public boolean removeStartupShortcut() {
- return deleteFile(System.getProperty("user.home") + "/.config/autostart/" + getShortcutName());
+ return writeArrayToFile(STARTUP + getShortcutName(), new String[]{
+ "[Desktop Entry]",
+ "Type=Application",
+ "Name=" + Constants.ABOUT_TITLE + " (Disabled)",
+ "Exec=/bin/true",
+ "Icon=" + DELETED_ICON,
+ "Terminal=false",
+ });
}
@Override
public boolean removeDesktopShortcut() {
- return deleteFile(System.getProperty("user.home") + "/Desktop/" + getShortcutName());
+ return deleteFile(DESKTOP + getShortcutName());
}
@Override
public boolean hasStartupShortcut() {
- String target = System.getProperty("user.home") + "/.config/autostart/";
- upgradeLegacyShortcut(target);
- return fileExists(target + getShortcutName());
+ try {
+ String contents = FileUtilities.readLocalFile(STARTUP + getShortcutName());
+ return !contents.contains("Icon=" + DELETED_ICON);
+ } catch (Exception ex) {
+ log.warn("Unable to read startup shortcut {}", STARTUP + getShortcutName(), ex);
+ }
+ return false;
}
@Override
public boolean hasDesktopShortcut() {
- String target = System.getProperty("user.home") + "/Desktop/";
- upgradeLegacyShortcut(target);
- return fileExists(target + getShortcutName());
+ // Upgrade legacy "QZ Tray.desktop" shortcuts
+ String shortcut = DESKTOP + Constants.ABOUT_TITLE + ".desktop";
+ if (fileExists(shortcut)) {
+ if (ShellUtilities.execute(new String[] { "rm", shortcut })) {
+ copyShortcut(appLauncher, DESKTOP);
+ }
+ }
+ return fileExists(DESKTOP + getShortcutName());
}
- /**
- * Creates a Linux ".desktop" shortcut
- *
- * @param target target location of shortcut
- * @return Whether or not the shortcut was created successfully
- */
- public boolean createShortcut(String target) {
+ private static boolean copyShortcut(String source, String target) {
return ShellUtilities.execute(new String[] {
- "cp", getAppPath(), target
+ "cp", source, target
});
}
@Override
- public String getShortcutName() {
- return Constants.PROPS_FILE + ".desktop";
- }
-
- /**
- * Returns the path to the jar executable or desktop launcher
- * @return
- */
- public String getAppPath() {
- return "/usr/share/applications/" + getShortcutName();
+ public void setShortcutName(String name) {
+ super.setShortcutName(name);
+ // Fix window titles on Gnome 3 per JDK-6528430
+ try {
+ Toolkit t = Toolkit.getDefaultToolkit();
+ Field f = t.getClass().getDeclaredField("awtAppClassName");
+ f.setAccessible(true);
+ f.set(t, name);
+ }
+ catch (Exception ignore) {}
}
- /**
- * Upgrade 1.9 shortcut to new 2.0 format
- * @return
- */
- private boolean upgradeLegacyShortcut(String target) {
- String shortcut = target + Constants.ABOUT_TITLE + ".desktop";
- if (fileExists(shortcut)) {
- if (ShellUtilities.execute(new String[] { "rm", shortcut })) {
- return createShortcut(target);
- }
- }
- return false;
+ @Override
+ public String getShortcutName() {
+ return Constants.PROPS_FILE + ".desktop";
}
}
diff --git a/src/qz/deploy/MacDeploy.java b/src/qz/deploy/MacDeploy.java
index 9b295fa90..ac8d82c99 100644
--- a/src/qz/deploy/MacDeploy.java
+++ b/src/qz/deploy/MacDeploy.java
@@ -11,41 +11,44 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import qz.utils.FileUtilities;
import qz.utils.ShellUtilities;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.IOException;
+import java.io.FileWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/**
* @author Tres Finocchiaro
*/
-public class MacDeploy extends DeployUtilities {
+class MacDeploy extends DeployUtilities {
private static final Logger log = LoggerFactory.getLogger(MacDeploy.class);
+ private String autostartToggle = getAppPath() + "/.autostart";
+ private String desktopShortcut = System.getProperty("user.home") + "/Desktop/" + getShortcutName();
+
@Override
public boolean hasStartupShortcut() {
- return ShellUtilities.executeAppleScript(
- "tell application \"System Events\" to get the name "
- + "of every login item where name is \"" + getShortcutName() + "\"",
- getShortcutName()
- );
+ removeLegacyStartup();
+ try {
+ return FileUtilities.readLocalFile(autostartToggle).contains("1");
+ } catch (Exception err) {
+ log.warn("Could not read .autostart file", err);
+ }
+ return false;
}
@Override
public boolean hasDesktopShortcut() {
- return isSymlink(System.getProperty("user.home") + "/Desktop/" + getShortcutName());
+ return FileUtilities.isSymlink(desktopShortcut);
}
@Override
public boolean createStartupShortcut() {
- return ShellUtilities.executeAppleScript(
- "tell application \"System Events\" to make login item "
- + "at end with properties {path:\"" + getAppPath() + "\", "
- + "hidden:true, name:\"" + getShortcutName() + "\"}"
- );
+ return writeAutostart(1);
}
@Override
@@ -61,28 +64,26 @@ public String getJarPath() {
return jarPath;
}
- /**
- * Returns the jar's filename, which may be the identity of the startup
- * entry
- *
- * @return The jar's filename without the preceeding path information
- */
private String getJarName() {
return new File(getJarPath()).getName();
}
@Override
public boolean createDesktopShortcut() {
-
-
- return ShellUtilities.execute(new String[] {
- "ln", "-sf", getAppPath(),
- System.getProperty("user.home") + "/Desktop/" + getShortcutName()
- });
+ return ShellUtilities.execute(new String[] {"ln", "-sf", getAppPath(), desktopShortcut});
}
@Override
public boolean removeStartupShortcut() {
+ return writeAutostart(0);
+ }
+
+ @Override
+ public boolean removeDesktopShortcut() {
+ return ShellUtilities.execute(new String[] {"unlink", desktopShortcut});
+ }
+
+ private boolean removeLegacyStartup() {
return ShellUtilities.executeAppleScript(
"tell application \"System Events\" to delete "
+ "every login item where name is \"" + getShortcutName() + "\" or "
@@ -90,44 +91,20 @@ public boolean removeStartupShortcut() {
);
}
- @Override
- public boolean removeDesktopShortcut() {
- return ShellUtilities.execute(new String[] {
- "unlink",
- System.getProperty("user.home") + "/Desktop/" + getShortcutName()
- });
- }
-
- /**
- * Determines if the specified file is a symbolic link.
- *
- * @param filePath path of file to check for symbolic link
- * @return true if a symbolic link is found
- */
- public static boolean isSymlink(String filePath) {
- log.info("Verifying symbolic link: {}", filePath);
- boolean returnVal = false;
- if (filePath != null) {
- File f = new File(filePath);
- if (f.exists()) {
- try {
- File canonicalFile = (f.getParent() == null? f:f.getParentFile().getCanonicalFile());
- returnVal = !canonicalFile.getCanonicalFile().equals(canonicalFile.getAbsoluteFile());
- }
- catch(IOException ex) {
- log.error("IOException checking for symlink", ex);
- }
- }
+ private boolean writeAutostart(int i) {
+ try(BufferedWriter w = new BufferedWriter(new FileWriter(autostartToggle))) {
+ w.write("" + i);
+ return true;
+ } catch(Exception err) {
+ log.warn("Could not write to .autostart", err);
}
-
- log.info("Symbolic link result: {}", returnVal);
- return returnVal;
+ return false;
}
/**
* Returns path to executable jar or app bundle
*/
- public String getAppPath() {
+ private String getAppPath() {
String target = getJarPath();
if (target.contains("/Applications/")) {
// Use the parent folder instead i.e. "/Applications/QZ Tray.app"
diff --git a/src/qz/deploy/WindowsDeploy.java b/src/qz/deploy/WindowsDeploy.java
index 3823e19a9..a91a04ce9 100644
--- a/src/qz/deploy/WindowsDeploy.java
+++ b/src/qz/deploy/WindowsDeploy.java
@@ -12,7 +12,6 @@
package qz.deploy;
import mslinks.ShellLink;
-import qz.common.Constants;
import qz.utils.ShellUtilities;
import java.io.IOException;
@@ -21,20 +20,11 @@
* @author Tres Finocchiaro
*/
public class WindowsDeploy extends DeployUtilities {
-
- // Try using ${windows.icon} first, if it exists
- private static String qzIcon = System.getenv("programfiles").replace(" (x86)", "") + "\\" + Constants.ABOUT_TITLE + "\\windows-icon.ico";
- private static String defaultIcon = System.getenv("windir") + "\\system32\\SHELL32.dll";
- private static boolean useQzIcon = fileExists(qzIcon);
+ private static String DELETED_ICON = System.getenv("windir") + "\\system32\\SHELL32.dll";
@Override
public boolean createStartupShortcut() {
- return ShellUtilities.executeRegScript(
- "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",
- "add",
- getShortcutName(),
- quoteWrap(getAppPath())
- );
+ return createShortcut(getStartupDirectory());
}
@Override
@@ -49,11 +39,17 @@ public boolean createDesktopShortcut() {
@Override
public boolean removeStartupShortcut() {
- return ShellUtilities.executeRegScript(
- "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",
- "delete",
- getShortcutName()
- );
+ try {
+ // Dummy shortcut
+ ShellLink link = ShellLink.createLink("%SystemRoot%\\system32\\rundll32.exe", getStartupDirectory() + getShortcutName() + ".lnk");
+ link.setIconLocation(DELETED_ICON);
+ link.getHeader().setIconIndex(131);
+ link.saveTo(getStartupDirectory() + getShortcutName() + ".lnk");
+ return true;
+ } catch (Exception ex) {
+ log.warn("Error removing startup shortcut", ex);
+ return false;
+ }
}
@Override
@@ -64,11 +60,16 @@ public boolean removeDesktopShortcut() {
@Override
public boolean hasStartupShortcut() {
- return ShellUtilities.executeRegScript(
- "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\",
- "query",
- getShortcutName()
- );
+ try {
+ ShellLink link = new ShellLink(getStartupDirectory() + getShortcutName() + ".lnk");
+ return link.getIconLocation() == null || !link.getIconLocation().equals(DELETED_ICON);
+ } catch (NegativeArraySizeException ignore) {
+ // Per mslinks issues #4
+ return true;
+ } catch (Exception ex) {
+ log.warn("Error detecting startup shortcut", ex);
+ return false;
+ }
}
@Override
@@ -77,9 +78,8 @@ public boolean hasDesktopShortcut() {
}
/**
- * Enables websockets in IE/Edge by unchecking "Include all local (intranet) sites not listed in other zones"
- * This has no effect on domain networks with "Automatically detect intranet network" checked.
- * In addition, Edge requires CheckNetIsolation command to be effective.
+ * Remove flag "Include all local (intranet) sites not listed in other zones". Requires CheckNetIsolation
+ * to be effective; Has no effect on domain networks with "Automatically detect intranet network" checked.
*
* @return true if successful
*/
@@ -90,20 +90,11 @@ public static boolean configureIntranetZone() {
// If the above value is set, remove it using bitwise XOR, thus disabling this setting
int data = ShellUtilities.getRegistryDWORD(path, name);
- if (data != -1) {
- if ((data & value) == value) {
- return ShellUtilities.setRegistryDWORD(path, name, data ^ value);
- }
- return true; // already set
- }
- return false;
+ return data != -1 && ((data & value) != value || ShellUtilities.setRegistryDWORD(path, name, data ^ value));
}
/**
- * Legacy Edge version: Configure loopback connections via
- * - about:flags > Developer Settings > Allow localhost loopback
- *
- * Modern Edge versions: Utilize CheckNetIsolation.exe via desktop installer instead.
+ * Set legacy Edge flag: about:flags > Developer Settings > Allow localhost loopback
*
* @return true if successful
*/
@@ -114,23 +105,7 @@ public static boolean configureEdgeLoopback() {
// If the above value does not exist, add it using bitwise OR, thus enabling this setting
int data = ShellUtilities.getRegistryDWORD(path, name);
- if (data != -1) {
- if ((data & value) != value) {
- return ShellUtilities.setRegistryDWORD(path, name, data | value);
- }
- return true; // already set
- }
- return false;
- }
-
- /**
- * Returns the string with Windows formatted escaped double quotes, useful for
- * inserting registry keys
- *
- * @return The supplied string wrapped in double quotes
- */
- private String quoteWrap(String text) {
- return "\\\"" + text + "\\\"";
+ return data != -1 && ((data & value) == value || ShellUtilities.setRegistryDWORD(path, name, data | value));
}
/**
@@ -155,4 +130,12 @@ private boolean createShortcut(String folderPath) {
private String getAppPath() {
return fixWhitespaces(getJarPath()).replaceAll(".jar$", ".exe");
}
+
+ private static String getStartupDirectory() {
+ if (System.getenv("programdata") == null) {
+ // XP
+ return System.getenv("allusersprofile") + "\\Start Menu\\Programs\\Startup\\";
+ }
+ return System.getenv("programdata") + "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\";
+ }
}
diff --git a/src/qz/utils/FileUtilities.java b/src/qz/utils/FileUtilities.java
index ca6d5d7fe..1c2bb4bcd 100644
--- a/src/qz/utils/FileUtilities.java
+++ b/src/qz/utils/FileUtilities.java
@@ -96,6 +96,25 @@ public static boolean isBadPath(String path) {
return path.contains(SystemUtilities.getDataDirectory());
}
+ public static boolean isSymlink(String filePath) {
+ log.info("Verifying symbolic link: {}", filePath);
+ boolean returnVal = false;
+ if (filePath != null) {
+ File f = new File(filePath);
+ if (f.exists()) {
+ try {
+ File canonicalFile = (f.getParent() == null? f:f.getParentFile().getCanonicalFile());
+ returnVal = !canonicalFile.getCanonicalFile().equals(canonicalFile.getAbsoluteFile());
+ }
+ catch(IOException ex) {
+ log.error("IOException checking for symlink", ex);
+ }
+ }
+ }
+
+ log.info("Symbolic link result: {}", returnVal);
+ return returnVal;
+ }
public static String readLocalFile(String file) throws IOException {
return new String(readFile(new DataInputStream(new FileInputStream(file))), Charsets.UTF_8);