177 lines
6.1 KiB
Go
177 lines
6.1 KiB
Go
//go:build darwin
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/VeraeLLC/PrintToVeraeTimeStamp/internal/settings"
|
|
"github.com/VeraeLLC/PrintToVeraeTimeStamp/internal/vlog"
|
|
)
|
|
|
|
// macOS in-installer key registration, run by the pkg postinstall as the
|
|
// console user (launchctl asuser + sudo -H -u). Native AppleScript dialogs;
|
|
// passwords come back through osascript's stdout pipe only — never argv,
|
|
// never files, never logs. Keys are logged masked only.
|
|
|
|
const dlgTitle = "Verae Print Stamp"
|
|
|
|
const (
|
|
machineWord = "Mac"
|
|
keyStoreDesc = "Keychain + ~/.verae"
|
|
)
|
|
|
|
func openURL(u string) { _ = exec.Command("open", u).Start() }
|
|
|
|
func guiAvailable() bool { return true }
|
|
|
|
// AppleScript gets all user-visible text via argv (no string interpolation →
|
|
// no injection). Output: "<button>\t<text returned>" or "CANCEL".
|
|
const dialogScript = `on run argv
|
|
set msg to item 1 of argv
|
|
set defAns to item 2 of argv
|
|
set hide to (item 3 of argv) is "1"
|
|
set btns to paragraphs of (item 4 of argv)
|
|
set defBtn to item 5 of argv
|
|
set useAnswer to (item 6 of argv) is "1"
|
|
tell me to activate
|
|
try
|
|
if useAnswer then
|
|
if hide then
|
|
set r to display dialog msg default answer defAns with hidden answer buttons btns default button defBtn with title "` + dlgTitle + `" giving up after 900
|
|
else
|
|
set r to display dialog msg default answer defAns buttons btns default button defBtn with title "` + dlgTitle + `" giving up after 900
|
|
end if
|
|
if gave up of r then return "CANCEL"
|
|
return (button returned of r) & tab & (text returned of r)
|
|
else
|
|
set r to display dialog msg buttons btns default button defBtn with title "` + dlgTitle + `" giving up after 900
|
|
if gave up of r then return "CANCEL"
|
|
return (button returned of r) & tab
|
|
end if
|
|
on error number -128
|
|
return "CANCEL"
|
|
end try
|
|
end run`
|
|
|
|
const chooseScript = `on run argv
|
|
set msg to item 1 of argv
|
|
set opts to paragraphs of (item 2 of argv)
|
|
tell me to activate
|
|
set r to choose from list opts with prompt msg default items {item 1 of opts} OK button name "Continue" cancel button name "Skip" with title "` + dlgTitle + `"
|
|
if r is false then return "CANCEL"
|
|
return item 1 of r
|
|
end run`
|
|
|
|
func osa(script string, args ...string) (string, error) {
|
|
cmd := exec.Command("osascript", append([]string{"-e", script, "--"}, args...)...)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("osascript: %v", err)
|
|
}
|
|
return strings.TrimRight(string(out), "\n"), nil
|
|
}
|
|
|
|
// ask shows a dialog; returns (button, answer).
|
|
func ask(msg, def string, hidden, withAnswer bool, buttons []string, defBtn string) (string, string, error) {
|
|
b2 := func(v bool) string {
|
|
if v {
|
|
return "1"
|
|
}
|
|
return "0"
|
|
}
|
|
out, err := osa(dialogScript, msg, def, b2(hidden), strings.Join(buttons, "\n"), defBtn, b2(withAnswer))
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if out == "CANCEL" {
|
|
return "", "", errCancel
|
|
}
|
|
btn, ans, _ := strings.Cut(out, "\t")
|
|
return btn, ans, nil
|
|
}
|
|
|
|
func say(msg string) { _, _, _ = ask(msg, "", false, false, []string{"OK"}, "OK") }
|
|
|
|
func choose(msg string, opts []string) (string, error) {
|
|
out, err := osa(chooseScript, msg, strings.Join(opts, "\n"))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if out == "CANCEL" {
|
|
return "", errCancel
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func runMacKeySetup(args []string) int {
|
|
fs := flag.NewFlagSet("mac-key-setup", flag.ContinueOnError)
|
|
askOpen := fs.Bool("ask-open", true, "also ask what to open after each print")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 2
|
|
}
|
|
vlog.Init("installer", false)
|
|
vlog.Printf("mac-key-setup start user=%s home=%s", os.Getenv("USER"), os.Getenv("HOME"))
|
|
release, ok := promptLock()
|
|
if !ok {
|
|
say("A Verae sign-in window is already open.")
|
|
return 1
|
|
}
|
|
result, saved := keyFlow("")
|
|
release()
|
|
vlog.Printf("mac-key-setup key result: %s", result)
|
|
fmt.Println("key:", result)
|
|
if saved || strings.HasPrefix(result, "kept") {
|
|
if n := len(listPending()); n > 0 {
|
|
done, remaining, _ := retryPending(stampFile)
|
|
vlog.Printf("mac-key-setup: retried held print jobs: %d stamped, %d remaining", done, remaining)
|
|
fmt.Printf("pending jobs: %d stamped, %d remaining\n", done, remaining)
|
|
}
|
|
}
|
|
if *askOpen {
|
|
mode := askOpenOnComplete()
|
|
fmt.Println("openOnComplete:", mode)
|
|
fmt.Println("promptForFilename:", askPromptForFilename())
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func askOpenOnComplete() string {
|
|
cur, _ := settings.Load()
|
|
def := map[string]string{settings.OpenFolder: "Show in Finder", settings.OpenPDF: "Open the PDF", settings.OpenNone: "Do nothing"}[cur.OpenOnComplete]
|
|
btn, _, err := ask("After each print is timestamped:\n\n• Show in Finder - reveal the new certificate PDF in Verae Timestamps (recommended)\n• Open the PDF - open the certificate in Preview\n• Do nothing", "", false, false, []string{"Do nothing", "Open the PDF", "Show in Finder"}, def)
|
|
mode := cur.OpenOnComplete
|
|
if err == nil {
|
|
mode = map[string]string{"Show in Finder": settings.OpenFolder, "Open the PDF": settings.OpenPDF, "Do nothing": settings.OpenNone}[btn]
|
|
}
|
|
if err := settings.SetOpenOnComplete(settings.UserPath(""), mode); err != nil {
|
|
vlog.Errorf("mac-key-setup: write config: %v", err)
|
|
} else {
|
|
vlog.Printf("mac-key-setup: openOnComplete=%s saved to %s", mode, settings.UserPath(""))
|
|
}
|
|
return mode
|
|
}
|
|
|
|
// askPromptForFilename: Save As dialog for every print (default on)?
|
|
func askPromptForFilename() bool {
|
|
cur, _ := settings.Load()
|
|
def := "Ask each time"
|
|
if !cur.Prompt() {
|
|
def = "Save automatically"
|
|
}
|
|
btn, _, err := ask("When you print to Verae Timestamping:\n\n• Ask each time - choose the file name and folder in a Save dialog (Cancel discards the print, nothing is stamped)\n• Save automatically - use the document title in Documents/Verae Timestamps", "", false, false, []string{"Save automatically", "Ask each time"}, def)
|
|
on := cur.Prompt()
|
|
if err == nil {
|
|
on = btn == "Ask each time"
|
|
}
|
|
if err := settings.SetPromptForFilename(settings.UserPath(""), map[bool]string{true: "true", false: "false"}[on]); err != nil {
|
|
vlog.Errorf("mac-key-setup: write config: %v", err)
|
|
} else {
|
|
vlog.Printf("mac-key-setup: promptForFilename=%v saved to %s", on, settings.UserPath(""))
|
|
}
|
|
return on
|
|
}
|