From 50f514ece452ae3c1f2f7aae5469b5e6f85fbcaf Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Fri, 31 May 2024 01:23:50 -0400 Subject: [PATCH] Fix Windows sync (WalkEntryDir now returns only UNIX-style separators); fix setting remote mod time on remote files when downloading; fix server binary compilation; fix init failing silently during SSH configuration --- libmuttonserver.go | 2 +- src/cli/entryList.go | 14 ++++---- src/cli/entryListUNIX.go | 13 -------- src/cli/entryListWIN.go | 13 -------- src/cli/init.go | 2 +- src/sync/client.go | 2 +- src/sync/common.go | 39 ---------------------- src/sync/commonUNIX.go | 49 ++++++++++++++++++++++++++++ src/sync/commonWIN.go | 50 +++++++++++++++++++++++++++++ src/sync/init.go | 4 +-- src/sync/server.go | 6 ++-- src/sync/serverPrintToStdoutUNIX.go | 12 ------- src/sync/serverPrintToStdoutWIN.go | 13 -------- 13 files changed, 114 insertions(+), 105 deletions(-) delete mode 100644 src/cli/entryListUNIX.go delete mode 100644 src/cli/entryListWIN.go create mode 100644 src/sync/commonUNIX.go create mode 100644 src/sync/commonWIN.go delete mode 100644 src/sync/serverPrintToStdoutUNIX.go delete mode 100644 src/sync/serverPrintToStdoutWIN.go diff --git a/libmuttonserver.go b/libmuttonserver.go index 616335b..719387d 100644 --- a/libmuttonserver.go +++ b/libmuttonserver.go @@ -35,7 +35,7 @@ func main() { fmt.Print(backend.EntryRoot + "\x1d" + strconv.FormatBool(backend.IsWindows)) case "init": // create the necessary directories for libmuttonserver to function - backend.DirInit() + backend.DirInit(false) os.MkdirAll(backend.ConfigDir+backend.PathSeparator+"deletions", 0700) case "version", "-v": versionServer() diff --git a/src/cli/entryList.go b/src/cli/entryList.go index 540223a..cae5c26 100644 --- a/src/cli/entryList.go +++ b/src/cli/entryList.go @@ -21,11 +21,11 @@ func determineIndentation(skippedDirList []bool, dirList []string, currentDirInd var lastPrefixIndex int // tracks the index (in both skippedDirList and dirList) of the last displayed parent directory var trimmedDirectory = dirList[currentDirIndex] - // determine initial indentation multiplier based on PathSeparator occurrences - indent := strings.Count(trimmedDirectory, backend.PathSeparator) - 1 // subtract 1 to avoid indenting root-level directories + // determine initial indentation multiplier based on "/" occurrences + indent := strings.Count(trimmedDirectory, "/") - 1 // subtract 1 to avoid indenting root-level directories for i, skipped := range skippedDirList[:currentDirIndex] { // checks each skipped directory to determine if it is a parent to the current directory - if strings.HasPrefix(trimmedDirectory, dirList[i]+backend.PathSeparator) { // if the current directory is the child of this iteration's directory... + if strings.HasPrefix(trimmedDirectory, dirList[i]+"/") { // if the current directory is the child of this iteration's directory... if skipped { // ...and this iteration's directory was skipped... subtractor++ // increment the subtractor to indicate that the visual indentation should be reduced } else { @@ -106,7 +106,7 @@ func EntryListGen() { // check if next directory is within the current one if dirListLength > i+1 { - if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, backend.PathSeparator)] { + if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, "/")] { containsSubdirectory = true } else { containsSubdirectory = false @@ -120,14 +120,14 @@ func EntryListGen() { for _, file := range fileList { // print the current file if it belongs in the current directory - otherwise, break the loop and move on to the next directory - if lastSlash := strings.LastIndex(file, backend.PathSeparator) + 1; file[:lastSlash-1] == directory { + if lastSlash := strings.LastIndex(file, "/") + 1; file[:lastSlash-1] == directory { // print directory header if this is the first run of the loop if !containsFiles { containsFiles = true skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier - printDirectoryHeader(vanityDirectory, indent) + fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", vanityDirectory) } charCounter, colorAlternator = printFileEntry(file, lastSlash, charCounter, indent, colorAlternator) @@ -139,7 +139,7 @@ func EntryListGen() { if dirListLength > 1 { // and directories besides the root-level exist... display directory header and empty directory warning skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier - printDirectoryHeader(vanityDirectory, indent) + fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", vanityDirectory) fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + backend.AnsiReset) } else { // warn if the only thing that exists is the root-level directory fmt.Print("\n\nNothing's here! For help creating your first entry, run \"mutn help\".") diff --git a/src/cli/entryListUNIX.go b/src/cli/entryListUNIX.go deleted file mode 100644 index b2a3a3c..0000000 --- a/src/cli/entryListUNIX.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !windows - -package cli - -import ( - "fmt" - "github.com/rwinkhart/MUTN/src/backend" - "strings" -) - -func printDirectoryHeader(vanityDirectory string, indent int) { - fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", vanityDirectory) -} diff --git a/src/cli/entryListWIN.go b/src/cli/entryListWIN.go deleted file mode 100644 index b5cf6b7..0000000 --- a/src/cli/entryListWIN.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build windows - -package cli - -import ( - "fmt" - "github.com/rwinkhart/MUTN/src/backend" - "strings" -) - -func printDirectoryHeader(vanityDirectory string, indent int) { - fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", strings.ReplaceAll(vanityDirectory, backend.PathSeparator, "/")) -} diff --git a/src/cli/init.go b/src/cli/init.go index dd4dd4b..a74ff35 100644 --- a/src/cli/init.go +++ b/src/cli/init.go @@ -40,7 +40,7 @@ func TempInitCli() { sshKeyProtected := inputBinary("Is the identity file password-protected?") // write config file - backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID, "sshUser": sshUser, "sshIP": sshIP, "sshPort": sshPort, "sshKey": sshKey, "sshKeyProtected": strconv.FormatBool(sshKeyProtected)}, false) + backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID, "sshUser": sshUser, "sshIP": sshIP, "sshPort": sshPort, "sshKey": sshKey, "sshKeyProtected": strconv.FormatBool(sshKeyProtected), "sshEntryRoot": "null", "sshIsWindows": "null"}, false) // generate device ID sshEntryRoot, sshIsWindows := sync.DeviceIDGen() diff --git a/src/sync/client.go b/src/sync/client.go index e50f8d1..93941e8 100644 --- a/src/sync/client.go +++ b/src/sync/client.go @@ -260,7 +260,7 @@ func sftpSync(downloadList, uploadList []string, manualSync bool) { localFile.Close() // set the modification time of the local file to match the value saved from the remote file (from before the download) - err = os.Chtimes(remoteEntryFullPath, time.Now(), modTime) + err = os.Chtimes(localEntryFullPath, time.Now(), modTime) } if filesTransfered { diff --git a/src/sync/common.go b/src/sync/common.go index 022aacb..83c7c33 100644 --- a/src/sync/common.go +++ b/src/sync/common.go @@ -3,49 +3,10 @@ package sync import ( "fmt" "github.com/rwinkhart/MUTN/src/backend" - "io/fs" "os" - "path/filepath" "strings" ) -// WalkEntryDir walks the entry directory and returns lists of all files and directories found (two separate lists) -func WalkEntryDir() ([]string, []string) { - // define file/directory containing slices so that they may be accessed by the anonymous WalkDir function - var fileList []string - var dirList []string - - // walk entry directory - _ = filepath.WalkDir(backend.EntryRoot, - func(fullPath string, entry fs.DirEntry, err error) error { - - // check for errors encountered while walking directory - if err != nil { - if os.IsNotExist(err) { - fmt.Println(backend.AnsiError+"The entry directory does not exist - run \""+os.Args[0], "init"+"\" to create it"+backend.AnsiReset) // TODO implement init command for libmuttonserver - } else { - // otherwise, print the source of the error - fmt.Println(backend.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + backend.AnsiReset) - } - os.Exit(1) - } - - // trim root path from each path before storing - trimmedPath := fullPath[rootLength:] - - // create separate slices for entries and directories - if !entry.IsDir() { - fileList = append(fileList, trimmedPath) - } else { - dirList = append(dirList, trimmedPath) - } - - return nil - }) - - return fileList, dirList -} - func getModTimes(entryList []string) []int64 { // get a list of all entry modification times var modList []int64 diff --git a/src/sync/commonUNIX.go b/src/sync/commonUNIX.go new file mode 100644 index 0000000..bc62632 --- /dev/null +++ b/src/sync/commonUNIX.go @@ -0,0 +1,49 @@ +//go:build !windows + +package sync + +import ( + "fmt" + "github.com/rwinkhart/MUTN/src/backend" + "io/fs" + "os" + "path/filepath" +) + +// WalkEntryDir walks the entry directory and returns lists of all files and directories found (two separate lists) +// regardless of platform, all paths are stored with forward slashes (UNIX-style) +func WalkEntryDir() ([]string, []string) { + // define file/directory containing slices so that they may be accessed by the anonymous WalkDir function + var fileList []string + var dirList []string + + // walk entry directory + _ = filepath.WalkDir(backend.EntryRoot, + func(fullPath string, entry fs.DirEntry, err error) error { + + // check for errors encountered while walking directory + if err != nil { + if os.IsNotExist(err) { + fmt.Println(backend.AnsiError+"The entry directory does not exist - run \""+os.Args[0], "init"+"\" to create it"+backend.AnsiReset) // TODO implement init command for libmuttonserver + } else { + // otherwise, print the source of the error + fmt.Println(backend.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + backend.AnsiReset) + } + os.Exit(1) + } + + // trim root path from each path before storing + trimmedPath := fullPath[rootLength:] + + // append the path to the appropriate slice + if !entry.IsDir() { + fileList = append(fileList, trimmedPath) + } else { + dirList = append(dirList, trimmedPath) + } + + return nil + }) + + return fileList, dirList +} diff --git a/src/sync/commonWIN.go b/src/sync/commonWIN.go new file mode 100644 index 0000000..b289ac3 --- /dev/null +++ b/src/sync/commonWIN.go @@ -0,0 +1,50 @@ +//go:build windows + +package sync + +import ( + "fmt" + "github.com/rwinkhart/MUTN/src/backend" + "io/fs" + "os" + "path/filepath" + "strings" +) + +// WalkEntryDir walks the entry directory and returns lists of all files and directories found (two separate lists) +// regardless of platform, all paths are stored with forward slashes (UNIX-style) +func WalkEntryDir() ([]string, []string) { + // define file/directory containing slices so that they may be accessed by the anonymous WalkDir function + var fileList []string + var dirList []string + + // walk entry directory + _ = filepath.WalkDir(backend.EntryRoot, + func(fullPath string, entry fs.DirEntry, err error) error { + + // check for errors encountered while walking directory + if err != nil { + if os.IsNotExist(err) { + fmt.Println(backend.AnsiError+"The entry directory does not exist - run \""+os.Args[0], "init"+"\" to create it"+backend.AnsiReset) // TODO implement init command for libmuttonserver + } else { + // otherwise, print the source of the error + fmt.Println(backend.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + backend.AnsiReset) + } + os.Exit(1) + } + + // trim root path from each path before storing and replace backslashes with forward slashes + trimmedPath := strings.ReplaceAll(fullPath[rootLength:], "\\", "/") + + // append the path to the appropriate slice + if !entry.IsDir() { + fileList = append(fileList, trimmedPath) + } else { + dirList = append(dirList, trimmedPath) + } + + return nil + }) + + return fileList, dirList +} diff --git a/src/sync/init.go b/src/sync/init.go index 4b556fa..00fa02e 100644 --- a/src/sync/init.go +++ b/src/sync/init.go @@ -14,8 +14,8 @@ func DeviceIDGen() (string, string) { deviceIDPrefix, _ := os.Hostname() deviceIDSuffix := backend.StringGen(rand.Intn(48)+48, false, 0) // TODO consider using complex string generator and removing unsafe characters manually deviceID := deviceIDPrefix + "-" + deviceIDSuffix - os.Create(backend.ConfigDir + backend.PathSeparator + "devices" + backend.PathSeparator + deviceID) // TODO remove existing device ID file if it exists (from both client and server) - sshEntryRootSSHIsWindows := strings.Split(GetSSHOutput("libmuttonserver register "+deviceID, false), "\x1d") // register device ID with server and fetch remote EntryRoot and OS type + os.Create(backend.ConfigDir + backend.PathSeparator + "devices" + backend.PathSeparator + deviceID) // TODO remove existing device ID file if it exists (from both client and server) + sshEntryRootSSHIsWindows := strings.Split(GetSSHOutput("libmuttonserver register "+deviceID, true), "\x1d") // register device ID with server and fetch remote EntryRoot and OS type; manualSync is true so the user is alerted if device ID registration fails return sshEntryRootSSHIsWindows[0], sshEntryRootSSHIsWindows[1] } diff --git a/src/sync/server.go b/src/sync/server.go index 4b8c2a0..d71bd77 100644 --- a/src/sync/server.go +++ b/src/sync/server.go @@ -23,7 +23,7 @@ func GetRemoteDataFromServer(clientDeviceID string) { // entry list for _, entry := range entryList { - printToStdout(entry) + fmt.Print("\x1f" + entry) } // modification time list @@ -36,7 +36,7 @@ func GetRemoteDataFromServer(clientDeviceID string) { // directory/folder list fmt.Print("\x1d") for _, dir := range dirList { - printToStdout(dir) + fmt.Print("\x1f" + dir) } // deletions list @@ -45,7 +45,7 @@ func GetRemoteDataFromServer(clientDeviceID string) { // print deletion if it is relevant to the current client device affectedIDTargetLocationIncomplete := strings.Split(deletion.Name(), "\x1d") if affectedIDTargetLocationIncomplete[0] == clientDeviceID { - fmt.Print("\x1f" + strings.ReplaceAll(affectedIDTargetLocationIncomplete[1], "\x1e", "/")) // do not use printToStdout as separators are filling in for \x1e + fmt.Print("\x1f" + strings.ReplaceAll(affectedIDTargetLocationIncomplete[1], "\x1e", "/")) // assume successful client deletion and remove deletions file (if assumption is somehow false, worst case scenario is that the client will re-upload the deleted entry) os.Remove(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + deletion.Name()) diff --git a/src/sync/serverPrintToStdoutUNIX.go b/src/sync/serverPrintToStdoutUNIX.go deleted file mode 100644 index b1979c4..0000000 --- a/src/sync/serverPrintToStdoutUNIX.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !windows - -package sync - -import ( - "fmt" -) - -// printToStdout prints a string to stdout with UNIX path separators -func printToStdout(targetLocationIncomplete string) { - fmt.Print("\x1f" + targetLocationIncomplete) -} diff --git a/src/sync/serverPrintToStdoutWIN.go b/src/sync/serverPrintToStdoutWIN.go deleted file mode 100644 index 2f8bd70..0000000 --- a/src/sync/serverPrintToStdoutWIN.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build windows - -package sync - -import ( - "fmt" - "strings" -) - -// printToStdout prints a string to stdout with UNIX path separators -func printToStdout(targetLocationIncomplete string) { - fmt.Print("\x1f" + strings.ReplaceAll(targetLocationIncomplete, "\\", "/")) -}