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);