diff --git a/libmuttonserver.go b/libmuttonserver.go index 74b1f16..0693668 100644 --- a/libmuttonserver.go +++ b/libmuttonserver.go @@ -2,9 +2,9 @@ package main import ( "bufio" + "encoding/json" "fmt" "os" - "strconv" "strings" "github.com/rwinkhart/go-boilerplate/back" @@ -110,8 +110,15 @@ func main() { } } } + // print EntryRoot, AgeDir and bool indicating OS type to stdout for client to store in config - fmt.Print(global.EntryRoot + global.FSSpace + global.AgeDir + global.FSSpace + strconv.FormatBool(global.IsWindows)) + registerResp := synccommon.RegisterResp{EntryRoot: global.EntryRoot, AgeDir: global.AgeDir, IsWindows: global.IsWindows} + registerRespBytes, err := json.Marshal(registerResp) + if err != nil { + fmt.Printf("{\"errMsg\":\"%s\"}", err.Error()) + return + } + fmt.Print(string(registerRespBytes)) case "init": // create the necessary directories for libmuttonserver to function _, err := global.DirInit(false) diff --git a/syncclient/client.go b/syncclient/client.go index c5146cf..9d6fd50 100644 --- a/syncclient/client.go +++ b/syncclient/client.go @@ -561,5 +561,6 @@ func RunJob(returnLists bool) ([3][]string, error) { if err != nil { return [3][]string{nil, nil, nil}, errors.New("unable to sync entries: " + err.Error()) } - return lists, nil // dummy return for when not returning lists + _ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it + return lists, nil // dummy return for when not returning lists } diff --git a/syncclient/oneOff.go b/syncclient/oneOff.go index e742f72..b3dd766 100644 --- a/syncclient/oneOff.go +++ b/syncclient/oneOff.go @@ -22,6 +22,7 @@ func ShearRemoteFromClient(vanityPath string, onlyShearAgeFile bool) error { } var modifier string + var output []byte sshClient, offlineMode, _, _, _, err := GetSSHClient() if offlineMode { goto end @@ -42,10 +43,13 @@ func ShearRemoteFromClient(vanityPath string, onlyShearAgeFile bool) error { if onlyShearAgeFile { modifier = "-age" } - _, err = GetSSHOutput(sshClient, "libmuttonserver shear"+modifier, deviceID+"\n"+strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) + output, err = GetSSHOutput(sshClient, "libmuttonserver shear"+modifier, deviceID+"\n"+strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) if err != nil { return errors.New("unable to shear target remotely: " + err.Error()) } + if len(output) > 11 { + return errors.New("unable to complete shear; server-side error occurred: " + string(output)[11:len(output)-2]) + } // close the SSH client err = sshClient.Close() @@ -78,6 +82,7 @@ func RenameRemoteFromClient(oldVanityPath, newVanityPath string) error { } // create an SSH client + var output []byte sshClient, offlineMode, _, _, _, err := GetSSHClient() if offlineMode { goto end @@ -87,13 +92,16 @@ func RenameRemoteFromClient(oldVanityPath, newVanityPath string) error { } // call the server to move the target on the remote system and add the old target to the deletions list - _, err = GetSSHOutput(sshClient, "libmuttonserver rename", + output, err = GetSSHOutput(sshClient, "libmuttonserver rename", (deviceIDList)[0].Name()+"\n"+ strings.ReplaceAll(oldVanityPath, global.PathSeparator, global.FSPath)+"\n"+ strings.ReplaceAll(newVanityPath, global.PathSeparator, global.FSPath)) if err != nil { return errors.New("unable to rename target remotely: " + err.Error()) } + if len(output) > 11 { + return errors.New("unable to complete rename; server-side error occurred: " + string(output)[11:len(output)-2]) + } // close the SSH client err = sshClient.Close() @@ -118,6 +126,7 @@ func AddFolderRemoteFromClient(vanityPath string) error { } // create an SSH client + var output []byte sshClient, offlineMode, _, _, _, err := GetSSHClient() if offlineMode { goto end @@ -127,10 +136,13 @@ func AddFolderRemoteFromClient(vanityPath string) error { } // call the server to create the folder remotely - _, err = GetSSHOutput(sshClient, "libmuttonserver addfolder", strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) // call the server to create the folder remotely + output, err = GetSSHOutput(sshClient, "libmuttonserver addfolder", strings.ReplaceAll(vanityPath, global.PathSeparator, global.FSPath)) // call the server to create the folder remotely if err != nil { return errors.New("unable to add folder remotely: " + err.Error()) } + if len(output) > 11 { + return errors.New("unable to complete addfolder; server-side error occurred: " + string(output)[11:len(output)-2]) + } // close the SSH client err = sshClient.Close() diff --git a/synccommon/common.go b/synccommon/common.go index ba496c7..cdfbc48 100644 --- a/synccommon/common.go +++ b/synccommon/common.go @@ -16,25 +16,31 @@ const ( AnsiDelete = "\033[38;5;1m" ) -// FetchResponse defines the structure of responses from `libmuttonserver fetch`. +// FetchResp defines the structure of responses from `libmuttonserver fetch`. type FetchResp struct { ErrMsg *string `json:"errMsg"` // nil if no error occurred ServerTime int64 `json:"serverTime"` Deletions []Deletion `json:"deletions"` FoldersToEntries map[string][]Entry `json:"folders"` } - type Deletion struct { VanityPath string `json:"vanityPath"` IsAgeFile bool `json:"isAgeFile"` } - type Entry struct { VanityPath string `json:"vanityPath"` ModTime int64 `json:"modTime"` AgeTimestamp *int64 `json:"ageTimestamp"` // nil if no age file is present (non-password entry) } +// RegisterResp defines the structure of responses from `libmuttonserver register` +type RegisterResp struct { + ErrMsg *string `json:"errMsg"` // nil if no error occurred + EntryRoot string `json:"entryRoot"` + AgeDir string `json:"ageDir"` + IsWindows bool `json:"isWindows"` +} + // GetModTimes returns a list of all entry modification times. func GetModTimes(entryList []string) []int64 { var modList []int64 @@ -70,17 +76,15 @@ func ShearLocal(vanityPath, clientDeviceID string, onlyShearAgeFile bool) (strin if !onlyShearAgeFile { f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"entry"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600) if err != nil { - // do not print error as there is currently no way of seeing server-side errors // failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical) - os.Exit(back.ErrorWrite) + return "", false, err } _ = f.Close() // error ignored; if the file could be created, it can probably be closed } f, err := os.OpenFile(global.ConfigDir+global.PathSeparator+"deletions"+global.PathSeparator+device.Name()+global.FSSpace+"age"+global.FSSpace+strings.ReplaceAll(vanityPath, "/", global.FSPath), os.O_CREATE|os.O_WRONLY, 0600) if err != nil { - // do not print error as there is currently no way of seeing server-side errors // failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical) - os.Exit(back.ErrorWrite) + return "", false, err } _ = f.Close() // error ignored; if the file could be created, it can probably be closed } diff --git a/synccycles/init.go b/synccycles/init.go index 6dd8389..44524e4 100644 --- a/synccycles/init.go +++ b/synccycles/init.go @@ -1,16 +1,17 @@ package synccycles import ( + "encoding/json" "errors" "math/rand" "os" "strconv" - "strings" "time" "github.com/rwinkhart/go-boilerplate/back" "github.com/rwinkhart/libmutton/global" "github.com/rwinkhart/libmutton/syncclient" + "github.com/rwinkhart/libmutton/synccommon" ) // DeviceIDGen generates a new client device ID and registers it with the server (will replace existing one). @@ -59,7 +60,15 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) { cleanupOnFail() return "", "", "", errors.New("unable to register device ID with server: " + err.Error()) } - sshEntryRootSSHAgeDirSSHIsWindows := strings.Split(string(output), global.FSSpace) + var registerResp synccommon.RegisterResp + err = json.Unmarshal(output, ®isterResp) + if err != nil { + cleanupOnFail() + return "", "", "", errors.New("unable to unmarshal server register response: " + err.Error()) + } + if registerResp.ErrMsg != nil { + return "", "", "", errors.New("unable to complete register; server-side error occurred: " + *registerResp.ErrMsg) + } _ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it // remove old device ID file (locally; may not exist) @@ -68,5 +77,5 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) { return "", "", "", errors.New("unable to remove old device ID file (locally): " + err.Error()) } - return sshEntryRootSSHAgeDirSSHIsWindows[0], sshEntryRootSSHAgeDirSSHIsWindows[1], sshEntryRootSSHAgeDirSSHIsWindows[2], nil + return registerResp.EntryRoot, registerResp.AgeDir, strconv.FormatBool(registerResp.IsWindows), nil }