From b03d7516186e7c2ba6c72fb5113e4394006bce60 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Thu, 30 May 2024 22:57:05 -0400 Subject: [PATCH] Save server EntryRoot and OS type during init, use to progress toward Windows server support --- libmuttonserver.go | 3 +++ src/cli/init.go | 5 ++++- src/sync/client.go | 26 ++++++++++++++++---------- src/sync/init.go | 10 +++++++--- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/libmuttonserver.go b/libmuttonserver.go index 88190a8..616335b 100644 --- a/libmuttonserver.go +++ b/libmuttonserver.go @@ -6,6 +6,7 @@ import ( "github.com/rwinkhart/MUTN/src/cli" "github.com/rwinkhart/MUTN/src/sync" "os" + "strconv" "strings" ) @@ -30,6 +31,8 @@ func main() { case "register": // register a new device ID os.Create(backend.ConfigDir + backend.PathSeparator + "devices" + backend.PathSeparator + args[2]) + // print EntryRoot and bool indicating OS type to stdout for client to store in config + fmt.Print(backend.EntryRoot + "\x1d" + strconv.FormatBool(backend.IsWindows)) case "init": // create the necessary directories for libmuttonserver to function backend.DirInit() diff --git a/src/cli/init.go b/src/cli/init.go index d727378..0a3d189 100644 --- a/src/cli/init.go +++ b/src/cli/init.go @@ -43,7 +43,10 @@ func TempInitCli() { backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID, "sshUser": sshUser, "sshIP": sshIP, "sshPort": sshPort, "sshKey": sshKey, "sshKeyProtected": strconv.FormatBool(sshKeyProtected)}) // generate device ID - sync.DeviceIDGen() + sshEntryRoot, sshIsWindows := sync.DeviceIDGen() + + // update config file with sshEntryRoot and sshIsWindows TODO append to existing config file + backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID, "sshUser": sshUser, "sshIP": sshIP, "sshPort": sshPort, "sshKey": sshKey, "sshKeyProtected": strconv.FormatBool(sshKeyProtected), "sshEntryRoot": sshEntryRoot, "sshIsWindows": sshIsWindows}) } else { // write config file backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID}) diff --git a/src/sync/client.go b/src/sync/client.go index cf4f0b4..90a6e72 100644 --- a/src/sync/client.go +++ b/src/sync/client.go @@ -19,18 +19,19 @@ const ( ansiUpload = "\033[38;5;4m" ) -// getSSHClient returns an SSH client connection to the server (also returns the remote username as a string) +// getSSHClient returns an SSH client connection to the server (also returns the remote EntryRoot as a string and the server's OS as a bool - IsWindows) // only supports key-based authentication (passphrases are supported for CLI-based implementations) -func getSSHClient(manualSync bool) (*ssh.Client, string) { +func getSSHClient(manualSync bool) (*ssh.Client, string, bool) { // get SSH config info, exit if not configured (displaying an error if the sync job was called manually) var sshUserConfig []string if manualSync { - sshUserConfig = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshKey", "sshKeyProtected"}, "SSH settings not configured - run \"mutn init\" to configure") + sshUserConfig = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshKey", "sshKeyProtected", "sshEntryRoot", "sshIsWindows"}, "SSH settings not configured - run \"mutn init\" to configure") } else { sshUserConfig = backend.ReadConfig([]string{"sshUser", "sshIP", "sshPort", "sshKey", "sshKeyProtected"}, "0") } - var user, ip, port, keyFile, keyFileProtected string + var user, ip, port, keyFile, keyFileProtected, entryRoot string + var isWindows bool for i, key := range sshUserConfig { switch i { case 0: @@ -43,6 +44,10 @@ func getSSHClient(manualSync bool) (*ssh.Client, string) { keyFile = key case 4: keyFileProtected = key + case 5: + entryRoot = key + case 6: + isWindows, _ = strconv.ParseBool(key) } } @@ -88,13 +93,13 @@ func getSSHClient(manualSync bool) (*ssh.Client, string) { os.Exit(1) } - return sshClient, user + return sshClient, entryRoot, isWindows } // GetSSHOutput runs a command over SSH and returns the output as a string -// TODO run getSSHClient() in RunJob and pass to each function that needs it (to avoid multiple connections), move defer to RunJob +// TODO run getSSHClient() ONCE in RunJob and pass to each function that needs it (to avoid multiple connections), move defer to RunJob func GetSSHOutput(cmd string, manualSync bool) string { - sshClient, _ := getSSHClient(manualSync) + sshClient, _, _ := getSSHClient(manualSync) defer sshClient.Close() // create a session @@ -189,9 +194,10 @@ func targetLocationFormatSFTP(targetName, serverEntryRoot string, serverIsWindow } // sftpSync takes two slices of entries (one for downloads and one for uploads) and syncs them between the client and server using SFTP +// TODO test Windows server hosting support func sftpSync(downloadList, uploadList []string, manualSync bool) { // establish an SSH connection for transfers - sshClient, sshUser := getSSHClient(manualSync) + sshClient, sshEntryRoot, sshIsWindows := getSSHClient(manualSync) defer sshClient.Close() // create an SFTP client @@ -210,7 +216,7 @@ func sftpSync(downloadList, uploadList []string, manualSync bool) { fmt.Println("Downloading " + ansiDownload + entryName + backend.AnsiReset) // store path to remote entry - remoteEntryFullPath := targetLocationFormatSFTP(entryName, "/home/"+sshUser+"/.local/share/libmutton", false) // TODO temporarily hard-coded to expect a default home folder location on a UNIX-like server + remoteEntryFullPath := targetLocationFormatSFTP(entryName, sshEntryRoot, sshIsWindows) // save modification time of remote file var fileInfo os.FileInfo @@ -287,7 +293,7 @@ func sftpSync(downloadList, uploadList []string, manualSync bool) { } // store path to remote entry - remoteEntryFullPath := targetLocationFormatSFTP(entryName, "/home/"+sshUser+"/.local/share/libmutton", false) // TODO temporarily hard-coded to expect a default home folder location on a UNIX-like server + remoteEntryFullPath := targetLocationFormatSFTP(entryName, sshEntryRoot, sshIsWindows) // create remote file var remoteFile *sftp.File diff --git a/src/sync/init.go b/src/sync/init.go index 49a9bf4..4b556fa 100644 --- a/src/sync/init.go +++ b/src/sync/init.go @@ -4,14 +4,18 @@ import ( "github.com/rwinkhart/MUTN/src/backend" "math/rand" "os" + "strings" ) // DeviceIDGen generates a new client device ID and registers it with the server // device IDs are only needed for online synchronization -func DeviceIDGen() { +// returns the remote EntryRoot and OS type (OS type is a bool: backend.IsWindows) +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) - GetSSHOutput("libmuttonserver register "+deviceID, false) // register device ID with server + 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 + + return sshEntryRootSSHIsWindows[0], sshEntryRootSSHIsWindows[1] }