From e9d0c3704362c8963e63a6c3b70d036f3e6c2493 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sat, 1 Feb 2025 20:48:03 -0500 Subject: [PATCH] Implement utility function for formatting and printing errors --- core/configParser.go | 13 ++-- core/copy.go | 15 ++--- core/copyDARWIN.go | 5 +- core/copyTERMUX.go | 5 +- core/copyUNIX.go | 7 +-- core/copyWIN.go | 4 +- core/gpg.go | 8 +-- core/init.go | 13 ++-- core/launchClipClearProcessCLIGeneric.go | 4 +- core/launchClipClearProcessCLIUNIX.go | 4 +- core/utilitiesMisc.go | 31 +++++---- sync/client.go | 80 ++++++++---------------- sync/common.go | 16 ++--- sync/commonUNIX.go | 7 +-- sync/commonWIN.go | 7 +-- sync/init.go | 10 +-- sync/oneOff.go | 11 +--- sync/server.go | 3 +- 18 files changed, 84 insertions(+), 159 deletions(-) diff --git a/core/configParser.go b/core/configParser.go index 9ee44af..5c0ddf9 100644 --- a/core/configParser.go +++ b/core/configParser.go @@ -13,8 +13,7 @@ import ( func loadConfig() *ini.File { cfg, err := ini.Load(ConfigPath) if err != nil { - fmt.Println(AnsiError+"Failed to load libmutton.ini:", err.Error()+AnsiReset) - os.Exit(ErrorRead) + PrintError("Failed to load libmutton.ini: "+err.Error(), ErrorRead, true) } return cfg } @@ -38,14 +37,12 @@ func ParseConfig(valuesRequested [][2]string, missingValueError string) ([]strin switch missingValueError { case "": err = fmt.Errorf("Failed to find value for key \"%s\" in section \"[%s]\" in libmutton.ini", pair[1], pair[0]) - fmt.Println(err.Error()) case "0": Exit(0) // hard (expected) exit for CLI; GUI/TUI continue silently default: err = fmt.Errorf("%s", missingValueError) - fmt.Println(err.Error()) } - Exit(ErrorRead) // hard exit for CLI; GUI/TUI continue silently + PrintError(err.Error(), ErrorRead, false) // if interactive (soft exit), return nil and the error to be handled by the caller return nil, err } @@ -63,8 +60,7 @@ func GenDeviceIDList(errorOnFail bool) *[]fs.DirEntry { deviceIDList, err := os.ReadDir(ConfigDir + PathSeparator + "devices") if err != nil { if errorOnFail { - fmt.Println(AnsiError+"Failed to read the devices directory:", err.Error()+AnsiReset) - os.Exit(ErrorRead) + PrintError("Failed to read the devices directory: "+err.Error(), ErrorRead, true) } else { return nil // a nil return value indicates that the devices directory could not be read/does not exist } @@ -113,7 +109,6 @@ func WriteConfig(valuesToWrite [][3]string, keysToPrune [][2]string, append bool // save to libmutton.ini err := cfg.SaveTo(ConfigPath) if err != nil { - fmt.Println(AnsiError+"Failed to save libmutton.ini:", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to save libmutton.ini: "+err.Error(), ErrorWrite, true) } } diff --git a/core/copy.go b/core/copy.go index 0dfc382..84add28 100644 --- a/core/copy.go +++ b/core/copy.go @@ -23,8 +23,7 @@ func CopyArgument(targetLocation string, field int) { // ensure field is not empty if decryptedEntry[field] == "" { - fmt.Println(AnsiError + "Field is empty" + AnsiReset) - os.Exit(ErrorTargetNotFound) + PrintError("Field is empty", ErrorTargetNotFound, true) } if field != 2 { @@ -50,8 +49,7 @@ func CopyArgument(targetLocation string, field int) { } } } else { - fmt.Println(AnsiError + "Field does not exist in entry" + AnsiReset) - os.Exit(ErrorTargetNotFound) + PrintError("Field does not exist in entry", ErrorTargetNotFound, true) } // copy field to clipboard, launch clipboard clearing process @@ -79,8 +77,7 @@ func clipClearProcess(assignedContents string) { clearClipboard := func() { err := cmdClear.Run() if err != nil { - fmt.Println(AnsiError+"Failed to clear clipboard:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to clear clipboard", ErrorClipboard, true) } Exit(0) } @@ -96,8 +93,7 @@ func clipClearProcess(assignedContents string) { newContents, err := cmdPaste.Output() if err != nil { - fmt.Println(AnsiError+"Failed to read clipboard contents:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to read clipboard contents", ErrorClipboard, true) } if assignedContents == strings.TrimRight(string(newContents), "\r\n") { @@ -117,8 +113,7 @@ func GenTOTP(secret string, time time.Time, forSteam bool) string { } if err != nil { - fmt.Println(AnsiError + "Error generating TOTP code" + AnsiReset) - os.Exit(ErrorOther) + PrintError("Error generating TOTP code", ErrorOther, true) } return totpToken diff --git a/core/copyDARWIN.go b/core/copyDARWIN.go index abceda8..d710009 100644 --- a/core/copyDARWIN.go +++ b/core/copyDARWIN.go @@ -3,8 +3,6 @@ package core import ( - "fmt" - "os" "os/exec" ) @@ -14,8 +12,7 @@ func copyString(continuous bool, copySubject string) { WriteToStdin(cmd, copySubject) err := cmd.Run() if err != nil { - fmt.Println(AnsiError+"Failed to copy to clipboard:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true) } if !continuous { diff --git a/core/copyTERMUX.go b/core/copyTERMUX.go index c4f18b6..b80f1c4 100644 --- a/core/copyTERMUX.go +++ b/core/copyTERMUX.go @@ -3,8 +3,6 @@ package core import ( - "fmt" - "os" "os/exec" ) @@ -14,8 +12,7 @@ func copyString(continuous bool, copySubject string) { WriteToStdin(cmd, copySubject) err := cmd.Run() if err != nil { - fmt.Println(AnsiError+"Failed to copy to clipboard:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true) } if !continuous { diff --git a/core/copyUNIX.go b/core/copyUNIX.go index 2447257..f63a76b 100644 --- a/core/copyUNIX.go +++ b/core/copyUNIX.go @@ -3,7 +3,6 @@ package core import ( - "fmt" "os" "os/exec" ) @@ -19,15 +18,13 @@ func copyString(continuous bool, copySubject string) { } else if _, envSet = os.LookupEnv("DISPLAY"); envSet { cmdCopy = exec.Command("xclip", "-sel", "c", "-t", "text/plain") } else { - fmt.Println(AnsiError + "Clipboard platform could not be determined - Note that the clipboard does not function in a raw TTY" + AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Clipboard platform could not be determined", ErrorClipboard, true) } WriteToStdin(cmdCopy, copySubject) err := cmdCopy.Run() if err != nil { - fmt.Println(AnsiError+"Failed to copy to clipboard:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true) } if !continuous { diff --git a/core/copyWIN.go b/core/copyWIN.go index bec85ad..29cf5f8 100644 --- a/core/copyWIN.go +++ b/core/copyWIN.go @@ -4,7 +4,6 @@ package core import ( "fmt" - "os" "os/exec" "strings" ) @@ -14,8 +13,7 @@ func copyString(continuous bool, copySubject string) { cmd := exec.Command("powershell.exe", "-c", fmt.Sprintf("echo '%s' | Set-Clipboard", strings.ReplaceAll(copySubject, "'", "''"))) err := cmd.Run() if err != nil { - fmt.Println(AnsiError+"Failed to copy to clipboard:", err.Error()+AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true) } if !continuous { diff --git a/core/gpg.go b/core/gpg.go index edc2a92..642838c 100644 --- a/core/gpg.go +++ b/core/gpg.go @@ -1,8 +1,6 @@ package core import ( - "fmt" - "os" "os/exec" "strings" ) @@ -18,8 +16,7 @@ func DecryptGPG(targetLocation string) []string { enableVirtualTerminalProcessing() if err != nil { - fmt.Println(AnsiError + "Failed to decrypt \"" + targetLocation + "\" - Ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly" + AnsiReset) - os.Exit(ErrorDecryption) + PrintError("Failed to decrypt \""+targetLocation+"\" - Ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly", ErrorDecryption, true) } return strings.Split(string(output), "\n") @@ -32,8 +29,7 @@ func EncryptGPG(input []string) []byte { WriteToStdin(cmd, strings.Join(input, "\n")) encryptedBytes, err := cmd.Output() if err != nil { - fmt.Println(AnsiError + "Failed to encrypt data - Ensure that your GPG key is valid and that you have a valid GPG ID set in libmutton.ini" + AnsiReset) - os.Exit(ErrorEncryption) + PrintError("Failed to encrypt data - Ensure that you have a valid GPG ID set in libmutton.ini", ErrorEncryption, true) } return encryptedBytes } diff --git a/core/init.go b/core/init.go index 8cce7c5..f23afd0 100644 --- a/core/init.go +++ b/core/init.go @@ -1,7 +1,6 @@ package core import ( - "fmt" "os" "os/exec" "strconv" @@ -45,8 +44,7 @@ func GpgKeyGen() string { cmd.Stdin = os.Stdin err := cmd.Run() if err != nil { - fmt.Println(AnsiError+"Failed to generate GPG key:", err.Error()+AnsiReset) - os.Exit(ErrorOther) + PrintError("Failed to generate GPG key: "+err.Error(), ErrorOther, true) } return "libmutton-" + unixTime + " (gpg-libmutton) " @@ -58,8 +56,7 @@ func DirInit(preserveOldConfigDir bool) string { // create EntryRoot err := os.MkdirAll(EntryRoot, 0700) if err != nil { - fmt.Println(AnsiError+"Failed to create \""+EntryRoot+"\":", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to create \""+EntryRoot+"\": "+err.Error(), ErrorWrite, true) } // get old device ID before its potential removal @@ -77,8 +74,7 @@ func DirInit(preserveOldConfigDir bool) string { if isAccessible { err = os.RemoveAll(ConfigDir) if err != nil { - fmt.Println(AnsiError+"Failed to remove existing config directory:", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to remove existing config directory: "+err.Error(), ErrorWrite, true) } } } @@ -86,8 +82,7 @@ func DirInit(preserveOldConfigDir bool) string { // create config directory w/devices subdirectory err = os.MkdirAll(ConfigDir+PathSeparator+"devices", 0700) if err != nil { - fmt.Println(AnsiError+"Failed to create \""+ConfigDir+"\":", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to create \""+ConfigDir+"\": "+err.Error(), ErrorWrite, true) } return oldDeviceID diff --git a/core/launchClipClearProcessCLIGeneric.go b/core/launchClipClearProcessCLIGeneric.go index 1ee21eb..44a7ba5 100644 --- a/core/launchClipClearProcessCLIGeneric.go +++ b/core/launchClipClearProcessCLIGeneric.go @@ -3,7 +3,6 @@ package core import ( - "fmt" "os" "os/exec" ) @@ -16,8 +15,7 @@ func LaunchClipClearProcess(copySubject string) { WriteToStdin(cmd, copySubject) err := cmd.Start() if err != nil { - fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?", ErrorClipboard, true) } os.Exit(0) // use os.Exit directly since this version of this function is only meant for non-interactive CLI implementations } diff --git a/core/launchClipClearProcessCLIUNIX.go b/core/launchClipClearProcessCLIUNIX.go index 345714c..40e83d3 100644 --- a/core/launchClipClearProcessCLIUNIX.go +++ b/core/launchClipClearProcessCLIUNIX.go @@ -3,7 +3,6 @@ package core import ( - "fmt" "os" "os/exec" "strconv" @@ -17,8 +16,7 @@ func LaunchClipClearProcess(copySubject string, isWayland bool) { WriteToStdin(cmd, copySubject) err := cmd.Start() if err != nil { - fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset) - os.Exit(ErrorClipboard) + PrintError("Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?", ErrorClipboard, true) } os.Exit(0) // use os.Exit directly since this version of this function is only meant for non-interactive CLI implementations } diff --git a/core/utilitiesMisc.go b/core/utilitiesMisc.go index 21854a0..38c6c96 100644 --- a/core/utilitiesMisc.go +++ b/core/utilitiesMisc.go @@ -18,21 +18,18 @@ func TargetIsFile(targetLocation string, errorOnFail bool, failCondition uint8) targetInfo, err := os.Stat(targetLocation) if err != nil { if errorOnFail { - fmt.Println(AnsiError + "Failed to access \"" + targetLocation + "\" - Ensure it exists and has the correct permissions" + AnsiReset) - os.Exit(ErrorTargetNotFound) + PrintError("Failed to access \""+targetLocation+"\" - Ensure it exists and has the correct permissions", ErrorTargetNotFound, true) } return false, false } if targetInfo.IsDir() { if errorOnFail && failCondition == 2 { - fmt.Println(AnsiError + "\"" + targetLocation + "\" is a directory" + AnsiReset) - os.Exit(ErrorTargetWrongType) + PrintError("\""+targetLocation+"\" is a directory", ErrorTargetWrongType, true) } return false, true } else { if errorOnFail && failCondition == 1 { - fmt.Println(AnsiError + "\"" + targetLocation + "\" is a file" + AnsiReset) - os.Exit(ErrorTargetWrongType) + PrintError("\""+targetLocation+"\" is a file", ErrorTargetWrongType, true) } return true, true } @@ -43,8 +40,7 @@ func WriteEntry(targetLocation string, entryData []string) { encryptedBytes := EncryptGPG(entryData) err := os.WriteFile(targetLocation, encryptedBytes, 0600) if err != nil { - fmt.Println(AnsiError+"Failed to write to file:", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to write to file: "+err.Error(), ErrorWrite, true) } } @@ -53,8 +49,7 @@ func WriteEntry(targetLocation string, entryData []string) { func WriteToStdin(cmd *exec.Cmd, input string) { stdin, err := cmd.StdinPipe() if err != nil { - fmt.Println(AnsiError+"Failed to access stdin for system command:", err.Error()+AnsiReset) - os.Exit(ErrorOther) + PrintError("Failed to access stdin for system command: "+err.Error(), ErrorOther, true) } go func() { @@ -69,8 +64,7 @@ func WriteToStdin(cmd *exec.Cmd, input string) { func CreateTempFile() *os.File { tempFile, err := os.CreateTemp("", "*.markdown") if err != nil { - fmt.Println(AnsiError+"Failed to create temporary file:", err.Error()+AnsiReset) - os.Exit(ErrorWrite) + PrintError("Failed to create temporary file: "+err.Error(), ErrorWrite, true) } return tempFile } @@ -182,3 +176,16 @@ func EntryIsNotEmpty(entryData []string) bool { func ExpandPathWithHome(path string) string { return strings.Replace(path, "~", Home, 1) } + +// PrintError prints an error message in the standard libmutton format and exits with the specified exit code. +// Requires: message (the error message to print), +// exitCode (the exit code to use), +// forceHardExit (if true, exit immediately; if false, allow soft exit for interactive clients). +func PrintError(message string, exitCode int, forceHardExit bool) { + fmt.Println(AnsiError + message + AnsiReset) + if forceHardExit { + os.Exit(exitCode) + } else { + Exit(exitCode) + } +} diff --git a/sync/client.go b/sync/client.go index b6479b5..cd70542 100644 --- a/sync/client.go +++ b/sync/client.go @@ -27,7 +27,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) { var sshUserConfig []string var missingValueError string if manualSync { - missingValueError = core.AnsiError + "SSH settings not fully configured" + core.AnsiReset + missingValueError = "SSH settings not fully configured" } else { missingValueError = "0" // allow silent exit at this point in offline mode } @@ -53,8 +53,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) { case 6: isWindows, err = strconv.ParseBool(key) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to parse server OS type:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to parse server OS type: "+err.Error(), core.ErrorRead, true) } } } @@ -62,8 +61,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) { // read private key key, err := os.ReadFile(keyFile) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to read private key file:", keyFile+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to read private key: "+keyFile, core.ErrorRead, true) } // parse private key @@ -74,16 +72,14 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) { parsedKey, err = ssh.ParsePrivateKeyWithPassphrase(key, core.PassphraseInputFunction("Enter passphrase for your SSH keyfile:")) } if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to parse private key:", keyFile+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to parse private key: "+keyFile, core.ErrorRead, true) } // read known hosts file var hostKeyCallback ssh.HostKeyCallback hostKeyCallback, err = knownhosts.New(core.Home + core.PathSeparator + ".ssh" + core.PathSeparator + "known_hosts") if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to read known hosts file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to read known hosts file: "+err.Error(), core.ErrorRead, true) } // configure SSH client @@ -99,8 +95,7 @@ func GetSSHClient(manualSync bool) (*ssh.Client, string, bool) { // connect to SSH server sshClient, err := ssh.Dial("tcp", ip+":"+port, sshConfig) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to connect to remote server:", err.Error()+core.AnsiReset) - core.Exit(core.ErrorServerConnection) // do not close/crash interactive clients + core.PrintError("Sync failed - Unable to connect to remote server: "+err.Error(), core.ErrorServerConnection, false) // do not crash/close interactive clients return nil, "", false } @@ -112,8 +107,7 @@ func GetSSHOutput(sshClient *ssh.Client, cmd, stdin string) string { // create a session sshSession, err := sshClient.NewSession() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to establish SSH session:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to establish SSH session: "+err.Error(), core.ErrorServerConnection, true) } // provide stdin data for session @@ -123,8 +117,7 @@ func GetSSHOutput(sshClient *ssh.Client, cmd, stdin string) string { var output []byte output, err = sshSession.CombinedOutput(cmd) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to run SSH command:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorSyncProcess) + core.PrintError("Sync failed - Unable to run SSH command: "+err.Error(), core.ErrorSyncProcess, true) } // convert the output to a string and remove leading/trailing whitespace @@ -140,8 +133,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string deviceIDList := core.GenDeviceIDList(true) if len(*deviceIDList) == 0 { if manualSync { - fmt.Println(core.AnsiError + "Sync failed - No device ID found" + core.AnsiReset) - os.Exit(core.ErrorTargetNotFound) + core.PrintError("Sync failed - No device ID found", core.ErrorTargetNotFound, true) } else { core.Exit(0) // exit silently if the sync job was called automatically, as the user may just be in offline mode } @@ -154,13 +146,11 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string // parse output/re-form lists if len(outputSlice) != 5 { // ensure information from server is complete - fmt.Println(core.AnsiError + "Sync failed - Unable to fetch remote data; server returned an unexpected response" + core.AnsiReset) - os.Exit(core.ErrorSyncProcess) + core.PrintError("Sync failed - Unable to fetch remote data; server returned an unexpected response", core.ErrorSyncProcess, true) } serverTime, err := strconv.ParseInt(outputSlice[0], 10, 64) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to parse server time:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to parse server time: "+err.Error(), core.ErrorRead, true) } entries := strings.Split(outputSlice[1], core.FSMisc)[1:] modsStrings := strings.Split(outputSlice[2], core.FSMisc)[1:] @@ -173,8 +163,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client, manualSync bool) (map[string for _, modString := range modsStrings { mod, err = strconv.ParseInt(modString, 10, 64) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to parse mod time:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to parse mod time: "+err.Error(), core.ErrorRead, true) } mods = append(mods, mod) } @@ -220,14 +209,12 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow // create an SFTP client from sshClient sftpClient, err := sftp.NewClient(sshClient) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to establish SFTP session:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to establish SFTP session: "+err.Error(), core.ErrorServerConnection, true) } defer func(sftpClient *sftp.Client) { err = sftpClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to close SFTP client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to close SFTP client: "+err.Error(), core.ErrorServerConnection, true) } }(sftpClient) @@ -245,8 +232,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var fileInfo os.FileInfo fileInfo, err = sftpClient.Stat(remoteEntryFullPath) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to get remote file info (mod time):", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to get remote file info (mod time): "+err.Error(), core.ErrorRead, true) } modTime := fileInfo.ModTime() @@ -254,8 +240,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var remoteFile *sftp.File remoteFile, err = sftpClient.Open(remoteEntryFullPath) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to open remote file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to open remote file: "+err.Error(), core.ErrorRead, true) } // store path to local entry @@ -265,15 +250,13 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var localFile *os.File localFile, err = os.OpenFile(localEntryFullPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to create local file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Sync failed - Unable to create local file: "+err.Error(), core.ErrorWrite, true) } // download the file _, err = remoteFile.WriteTo(localFile) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to download remote file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorSyncProcess) + core.PrintError("Sync failed - Unable to download remote file: "+err.Error(), core.ErrorSyncProcess, true) } // close the files @@ -302,8 +285,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var fileInfo os.FileInfo fileInfo, err = os.Stat(localEntryFullPath) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to get local file info (mod time):", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to get local file info (mod time): "+err.Error(), core.ErrorRead, true) } modTime := fileInfo.ModTime() @@ -311,8 +293,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var localFile *os.File localFile, err = os.Open(localEntryFullPath) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to open local file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Sync failed - Unable to open local file: "+err.Error(), core.ErrorRead, true) } // store path to remote entry @@ -322,15 +303,13 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow var remoteFile *sftp.File remoteFile, err = sftpClient.OpenFile(remoteEntryFullPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to create remote file ("+remoteEntryFullPath+"):", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Sync failed - Unable to create remote file ("+remoteEntryFullPath+"): "+err.Error(), core.ErrorWrite, true) } // upload the file _, err = localFile.WriteTo(remoteFile) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to upload local file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorSyncProcess) + core.PrintError("Sync failed - Unable to upload local file: "+err.Error(), core.ErrorSyncProcess, true) } // close the files @@ -340,8 +319,7 @@ func sftpSync(sshClient *ssh.Client, sshEntryRoot string, sshIsWindows bool, dow // set permissions on remote file err = sftpClient.Chmod(remoteEntryFullPath, 0600) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to set permissions on remote file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorSyncProcess) + core.PrintError("Sync failed - Unable to set permissions on remote file: "+err.Error(), core.ErrorSyncProcess, true) } // set the modification time of the remote file to match the value saved from the local file (from before the upload) @@ -410,8 +388,7 @@ func deletionSync(deletions []string) { fmt.Println(ansiDelete+deletion+core.AnsiReset, "has been sheared, removing locally (if it exists)") err := os.RemoveAll(core.TargetLocationFormat(deletion)) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Failed to shear "+deletion+" locally:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Sync failed - Failed to shear "+deletion+" locally: "+err.Error(), core.ErrorWrite, true) } } @@ -432,12 +409,10 @@ func folderSync(folders []string) { if !isFile && !isAccessible { err := os.MkdirAll(folderFullPath, 0700) if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Failed to create folder \""+folder+"\":", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Sync failed - Failed to create folder ("+folder+"): "+err.Error(), core.ErrorWrite, true) } } else if isFile { - fmt.Println(core.AnsiError + "Sync failed - Failed to create folder \"" + folder + "\" - A file with the same name already exists" + core.AnsiReset) - os.Exit(core.ErrorTargetExists) + core.PrintError("Sync failed - Failed to create folder ("+folder+") - A file with the same name already exists", core.ErrorTargetExists, true) } } } @@ -454,8 +429,7 @@ func RunJob(manualSync, returnLists bool) [3][]string { defer func(sshClient *ssh.Client) { err := sshClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to close SSH client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to close SSH client: "+err.Error(), core.ErrorServerConnection, true) } }(sshClient) diff --git a/sync/common.go b/sync/common.go index ba95037..045e0d0 100644 --- a/sync/common.go +++ b/sync/common.go @@ -1,7 +1,6 @@ package sync import ( - "fmt" "os" "strings" @@ -56,8 +55,7 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) (string, bool) } err := os.RemoveAll(targetLocationComplete) if err != nil { - fmt.Println(core.AnsiError+"Failed to remove local target:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Failed to remove local target: "+err.Error(), core.ErrorWrite, true) } if !onServer && len(*deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode) @@ -82,15 +80,13 @@ func RenameLocal(oldLocationIncomplete, newLocationIncomplete string, verifyOldL // ensure newLocation does not exist _, isAccessible := core.TargetIsFile(newLocation, false, 0) if isAccessible { - fmt.Println(core.AnsiError + "\"" + newLocation + "\" already exists" + core.AnsiReset) - os.Exit(core.ErrorTargetExists) + core.PrintError("\""+newLocation+"\" already exists", core.ErrorTargetExists, true) } // rename oldLocation to newLocation err := os.Rename(oldLocation, newLocation) if err != nil { - fmt.Println(core.AnsiError + "Failed to rename - Does the target containing directory exist?" + core.AnsiReset) - os.Exit(core.ErrorTargetNotFound) + core.PrintError("Failed to rename - Does the target containing directory exist?", core.ErrorTargetNotFound, true) } // do not exit program, as this function is used as part of RenameRemoteFromClient @@ -104,11 +100,9 @@ func AddFolderLocal(targetLocationIncomplete string) { err := os.Mkdir(targetLocationComplete, 0700) if err != nil { if os.IsExist(err) { - fmt.Println(core.AnsiError + "Directory already exists" + core.AnsiReset) - os.Exit(core.ErrorTargetExists) + core.PrintError("Directory already exists", core.ErrorTargetExists, true) } else { - fmt.Println(core.AnsiError+"Failed to create directory:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Failed to create directory: "+err.Error(), core.ErrorWrite, true) } } diff --git a/sync/commonUNIX.go b/sync/commonUNIX.go index 5aa4006..b4f6cf4 100644 --- a/sync/commonUNIX.go +++ b/sync/commonUNIX.go @@ -3,7 +3,6 @@ package sync import ( - "fmt" "io/fs" "os" "path/filepath" @@ -25,12 +24,10 @@ func WalkEntryDir() ([]string, []string) { // check for errors encountered while walking directory if err != nil { if os.IsNotExist(err) { - fmt.Println(core.AnsiError + "The entry directory does not exist - Initialize libmutton to create it" + core.AnsiReset) + core.PrintError("The entry directory does not exist - Initialize libmutton to create it", core.ErrorOther, true) } else { - // otherwise, print the source of the error - fmt.Println(core.AnsiError+"An unexpected error occurred while generating the entry list:", err.Error()+core.AnsiReset) + core.PrintError("An unexpected error occurred while generating the entry list: "+err.Error(), core.ErrorOther, true) } - os.Exit(core.ErrorOther) } // trim root path from each path before storing diff --git a/sync/commonWIN.go b/sync/commonWIN.go index fe8994b..57f3a70 100644 --- a/sync/commonWIN.go +++ b/sync/commonWIN.go @@ -3,7 +3,6 @@ package sync import ( - "fmt" "io/fs" "os" "path/filepath" @@ -26,12 +25,10 @@ func WalkEntryDir() ([]string, []string) { // check for errors encountered while walking directory if err != nil { if os.IsNotExist(err) { - fmt.Println(core.AnsiError + "The entry directory does not exist - Initialize libmutton to create it" + core.AnsiReset) + core.PrintError("The entry directory does not exist - Initialize libmutton to create it", core.ErrorOther, true) } else { - // otherwise, print the source of the error - fmt.Println(core.AnsiError+"An unexpected error occurred while generating the entry list:", err.Error()+core.AnsiReset) + core.PrintError("An unexpected error occurred while generating the entry list: "+err.Error(), core.ErrorOther, true) } - os.Exit(core.ErrorOther) } // trim root path from each path before storing and replace backslashes with forward slashes diff --git a/sync/init.go b/sync/init.go index 109f262..cdccd9d 100644 --- a/sync/init.go +++ b/sync/init.go @@ -1,7 +1,6 @@ package sync import ( - "fmt" "math/rand" "os" "strconv" @@ -24,8 +23,7 @@ func DeviceIDGen(oldDeviceID string) (string, string) { // create new device ID file (locally) fileToClose, err := os.OpenFile(core.ConfigDir+core.PathSeparator+"devices"+core.PathSeparator+newDeviceID, os.O_CREATE|os.O_WRONLY, 0600) if err != nil { - fmt.Println(core.AnsiError+"Failed to create local device ID file:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Failed to create local device ID file: "+err.Error(), core.ErrorWrite, true) } _ = fileToClose.Close() // error ignored; if the file could be created, it can probably be closed @@ -36,15 +34,13 @@ func DeviceIDGen(oldDeviceID string) (string, string) { sshEntryRootSSHIsWindows := strings.Split(GetSSHOutput(sshClient, "libmuttonserver register", newDeviceID+"\n"+oldDeviceID), core.FSSpace) err = sshClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Init failed - Unable to close SSH client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Init failed - Unable to close SSH client: "+err.Error(), core.ErrorServerConnection, true) } // remove old device ID file (locally; may not exist) err = os.RemoveAll(core.ConfigDir + core.PathSeparator + "devices" + core.PathSeparator + oldDeviceID) if err != nil { - fmt.Println(core.AnsiError+"Failed to remove old device ID file (locally):", err.Error()+core.AnsiReset) - os.Exit(core.ErrorWrite) + core.PrintError("Failed to remove old device ID file (locally): "+err.Error(), core.ErrorWrite, true) } return sshEntryRootSSHIsWindows[0], sshEntryRootSSHIsWindows[1] diff --git a/sync/oneOff.go b/sync/oneOff.go index 3017b19..85886de 100644 --- a/sync/oneOff.go +++ b/sync/oneOff.go @@ -1,8 +1,6 @@ package sync import ( - "fmt" - "os" "strings" "github.com/rwinkhart/libmutton/core" @@ -28,8 +26,7 @@ func ShearRemoteFromClient(targetLocationIncomplete string, forceOffline bool) { // close the SSH client err := sshClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to close SSH client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to close SSH client: "+err.Error(), core.ErrorServerConnection, true) } } @@ -55,8 +52,7 @@ func RenameRemoteFromClient(oldLocationIncomplete, newLocationIncomplete string, // close the SSH client err := sshClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to close SSH client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to close SSH client: "+err.Error(), core.ErrorServerConnection, true) } } @@ -79,8 +75,7 @@ func AddFolderRemoteFromClient(targetLocationIncomplete string, forceOffline boo // close the SSH client err := sshClient.Close() if err != nil { - fmt.Println(core.AnsiError+"Sync failed - Unable to close SSH client:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorServerConnection) + core.PrintError("Sync failed - Unable to close SSH client: "+err.Error(), core.ErrorServerConnection, true) } } diff --git a/sync/server.go b/sync/server.go index 1f9eb40..7191dae 100644 --- a/sync/server.go +++ b/sync/server.go @@ -17,8 +17,7 @@ func GetRemoteDataFromServer(clientDeviceID string) { modList := getModTimes(entryList) deletionsList, err := os.ReadDir(core.ConfigDir + core.PathSeparator + "deletions") if err != nil { - fmt.Println(core.AnsiError+"Failed to read the deletions directory:", err.Error()+core.AnsiReset) - os.Exit(core.ErrorRead) + core.PrintError("Failed to read the deletions directory: "+err.Error(), core.ErrorRead, true) } // print the current UNIX timestamp to stdout