126 lines
4.7 KiB
Java
Executable File
126 lines
4.7 KiB
Java
Executable File
package qz.installer;
|
|
/**
|
|
* @author Tres Finocchiaro
|
|
*
|
|
* Copyright (C) 2019 Tres Finocchiaro, QZ Industries, LLC
|
|
*
|
|
* LGPL 2.1 This is free software. This software and source code are released under
|
|
* the "LGPL 2.1 License". A copy of this license should be distributed with
|
|
* this software. http://www.gnu.org/licenses/lgpl-2.1.html
|
|
*/
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import qz.utils.FileUtilities;
|
|
import qz.utils.ShellUtilities;
|
|
import qz.utils.SystemUtilities;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
import static qz.common.Constants.*;
|
|
|
|
public class MacInstaller extends Installer {
|
|
protected static final Logger log = LogManager.getLogger(MacInstaller.class);
|
|
private static final String PACKAGE_NAME = getPackageName();
|
|
public static final String LAUNCH_AGENT_PATH = String.format("/Library/LaunchAgents/%s.plist", MacInstaller.PACKAGE_NAME);
|
|
private String destination = "/Applications/" + ABOUT_TITLE + ".app";
|
|
|
|
public Installer addAppLauncher() {
|
|
// not needed; registered when "QZ Tray.app" is copied
|
|
return this;
|
|
}
|
|
|
|
public Installer addStartupEntry() {
|
|
File dest = new File(LAUNCH_AGENT_PATH);
|
|
HashMap<String, String> fieldMap = new HashMap<>();
|
|
// Dynamic fields
|
|
fieldMap.put("%PACKAGE_NAME%", PACKAGE_NAME);
|
|
fieldMap.put("%COMMAND%", String.format("%s/Contents/MacOS/%s", destination, ABOUT_TITLE));
|
|
fieldMap.put("%PARAM%", "--honorautostart");
|
|
|
|
try {
|
|
FileUtilities.configureAssetFile("assets/mac-launchagent.plist.in", dest, fieldMap, MacInstaller.class);
|
|
// Disable service until reboot
|
|
if(SystemUtilities.isMac()) {
|
|
ShellUtilities.execute("/bin/launchctl", "unload", MacInstaller.LAUNCH_AGENT_PATH);
|
|
}
|
|
} catch(IOException e) {
|
|
log.warn("Unable to write startup file: {}", dest, e);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public void setDestination(String destination) {
|
|
this.destination = destination;
|
|
}
|
|
|
|
public String getDestination() {
|
|
return destination;
|
|
}
|
|
|
|
public Installer addSystemSettings() {
|
|
// Chrome protocol handler
|
|
String plist = "/Library/Preferences/com.google.Chrome.plist";
|
|
if(ShellUtilities.execute(new String[] { "/usr/bin/defaults", "write", plist }, new String[] {DATA_DIR + "://*" }).isEmpty()) {
|
|
ShellUtilities.execute("/usr/bin/defaults", "write", plist, "URLAllowlist", "-array-add", DATA_DIR +"://*");
|
|
}
|
|
return this;
|
|
}
|
|
public Installer removeSystemSettings() {
|
|
// Remove startup entry
|
|
File dest = new File(LAUNCH_AGENT_PATH);
|
|
dest.delete();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Removes legacy (<= 2.0) startup entries
|
|
*/
|
|
public Installer removeLegacyStartup() {
|
|
log.info("Removing startup entries for all users matching " + ABOUT_TITLE);
|
|
String script = "tell application \"System Events\" to delete "
|
|
+ "every login item where name is \"" + ABOUT_TITLE + "\""
|
|
+ " or name is \"" + PROPS_FILE + ".jar\"";
|
|
|
|
// Run on background thread in case System Events is hung or slow to respond
|
|
final String finalScript = script;
|
|
new Thread(() -> {
|
|
ShellUtilities.executeAppleScript(finalScript);
|
|
}).run();
|
|
return this;
|
|
}
|
|
|
|
public static String getPackageName() {
|
|
String packageName;
|
|
String[] parts = ABOUT_URL.split("\\W");
|
|
if (parts.length >= 2) {
|
|
// Parse io.qz.qz-print from Constants
|
|
packageName = String.format("%s.%s.%s", parts[parts.length - 1], parts[parts.length - 2], PROPS_FILE);
|
|
} else {
|
|
// Fallback on something sane
|
|
packageName = "local." + PROPS_FILE;
|
|
}
|
|
return packageName;
|
|
}
|
|
|
|
public void spawn(List<String> args) throws Exception {
|
|
if(SystemUtilities.isAdmin()) {
|
|
// macOS unconventionally uses "$USER" during its install process
|
|
String sudoer = System.getenv("USER");
|
|
if(sudoer == null || sudoer.isEmpty() || sudoer.equals("root")) {
|
|
// Fallback, should only fire via Terminal + sudo
|
|
sudoer = ShellUtilities.executeRaw("logname").trim();
|
|
}
|
|
// Start directly without waitFor(...), avoids deadlocking
|
|
Runtime.getRuntime().exec(new String[] { "su", sudoer, "-c", "\"" + StringUtils.join(args, "\" \"") + "\""});
|
|
} else {
|
|
Runtime.getRuntime().exec(args.toArray(new String[args.size()]));
|
|
}
|
|
}
|
|
}
|