Files
quality_recticel/tray/src/qz/installer/MacInstaller.java
2025-10-05 14:32:47 -04:00

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()]));
}
}
}