From 96183890acf7fc9f26e50fd46df56707af334674 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 19 Feb 2017 16:20:43 +0100 Subject: [PATCH] Change of platform --- .gitignore | 48 ++++++++-------- CorrectPath.go | 18 +++--- LICENSE | 48 ++++++++-------- README.md | 50 ++++++++--------- ReadFlags.go | 32 +++++------ Sync/ComparePath.go | 14 ++--- Sync/ConnectSSH.go | 84 ++++++++++++++-------------- Sync/Constants.go | 10 ++-- Sync/KeyboardInteractiveChallenge.go | 70 +++++++++++------------ Sync/NormalisePath.go | 24 ++++---- Sync/PasswordCallback.go | 20 +++---- Sync/ReadYesNoAnswer.go | 46 +++++++-------- Sync/Variables.go | 30 +++++----- Sync/WalkerLocal.go | 20 +++---- Variables.go | 22 ++++---- 15 files changed, 268 insertions(+), 268 deletions(-) diff --git a/.gitignore b/.gitignore index daf913b..c5a0eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,24 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/CorrectPath.go b/CorrectPath.go index 856ef1b..06d71b0 100644 --- a/CorrectPath.go +++ b/CorrectPath.go @@ -1,9 +1,9 @@ -package main - -func correctPath(path string) string { - if path[len(path)-1:] == `/` || path[len(path)-1:] == `\` { - return path[:len(path)-1] - } else { - return path - } -} +package main + +func correctPath(path string) string { + if path[len(path)-1:] == `/` || path[len(path)-1:] == `\` { + return path[:len(path)-1] + } else { + return path + } +} diff --git a/LICENSE b/LICENSE index f80baa2..b6a566a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,24 @@ -Copyright (c) 2015, Thorsten Sommer -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +Copyright (c) 2015, Thorsten Sommer +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md index 5a4776b..a64c95e 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -# Sync -This is a simple SFTP synchronisation tool **without** any external dependencies e.g. PuTTY, WinSCP, OpenSSH. Just download the executable which matches your OS and architecture (32 vs. 64 bits) and run it. - -## Syntax -Sync provids a few arguments: -- ``localDir`` defines the local directory e.g. ``c:\Users\A\My Documents`` or ``/users/A/My Documents``. If you omit or set it to ``.``, the current working directory gets used. -- ``remoteDir`` is the remote directory e.g. ``/users/A/Sync``. -- ``server`` is the SSH server e.g. ``my-server.com:22``. -- Use ``user`` to set the user's name and ``pwd`` to set the user's password. You can omit the ``pwd``, thus, the program ask for the password on demand. -- With the argument ``supervised`` you can control if the program should ask you for every type of change. The default is the supervised operating mode. In order to disable it, use ``supervised=false``. -- Finnaly, the ``pushOnly`` argument can be used to set the **backup mode**, where all remote changes are ignored. The default is the enabled backup mode. In order to sync changes in both directions, set ``pushOnly=false``. - -## Features -- The whole code is open source and can be used for any purpose (also commercial) -- If you want, you can compile the code by your own by using the [Go](http://www.golang.org) -- The program just needs very low resources e.g. around 14 MB memory for Microsoft Windows 10 to sync roughly 6000 files. More files need more memory, because the meta information must keep in memory. -- If a connection cannot setup, the program re-tries it -- At the moment, SSHTunnel uses only the password authentication methods. Therefore, it is currently not possible to use e.g. a certificate, etc. Nevertheless, the implementation of this feature is possible. -- The configuration must be provided by using the command-line arguments. It is currently not possible to use e.g. a configuration file. -- You can avoid the password argument if you prefer to provide the password on demand. -- Sync can be used in **backup mode** where all remote changes are ignored or in the **full mode**, where both directions gets synchronised. -- Use the **supervised mode** in order to get the full control - -## Download -Go and get the latest release from the [release page](https://github.com/SommerEngineering/Sync/releases). Sync use the [SFTP library](https://github.com/pkg/sftp) from Dave Cheney. Thanks very much for the good work! +# Sync +This is a simple SFTP synchronisation tool **without** any external dependencies e.g. PuTTY, WinSCP, OpenSSH. Just download the executable which matches your OS and architecture (32 vs. 64 bits) and run it. + +## Syntax +Sync provids a few arguments: +- ``localDir`` defines the local directory e.g. ``c:\Users\A\My Documents`` or ``/users/A/My Documents``. If you omit or set it to ``.``, the current working directory gets used. +- ``remoteDir`` is the remote directory e.g. ``/users/A/Sync``. +- ``server`` is the SSH server e.g. ``my-server.com:22``. +- Use ``user`` to set the user's name and ``pwd`` to set the user's password. You can omit the ``pwd``, thus, the program ask for the password on demand. +- With the argument ``supervised`` you can control if the program should ask you for every type of change. The default is the supervised operating mode. In order to disable it, use ``supervised=false``. +- Finnaly, the ``pushOnly`` argument can be used to set the **backup mode**, where all remote changes are ignored. The default is the enabled backup mode. In order to sync changes in both directions, set ``pushOnly=false``. + +## Features +- The whole code is open source and can be used for any purpose (also commercial) +- If you want, you can compile the code by your own by using the [Go](http://www.golang.org) +- The program just needs very low resources e.g. around 14 MB memory for Microsoft Windows 10 to sync roughly 6000 files. More files need more memory, because the meta information must keep in memory. +- If a connection cannot setup, the program re-tries it +- At the moment, SSHTunnel uses only the password authentication methods. Therefore, it is currently not possible to use e.g. a certificate, etc. Nevertheless, the implementation of this feature is possible. +- The configuration must be provided by using the command-line arguments. It is currently not possible to use e.g. a configuration file. +- You can avoid the password argument if you prefer to provide the password on demand. +- Sync can be used in **backup mode** where all remote changes are ignored or in the **full mode**, where both directions gets synchronised. +- Use the **supervised mode** in order to get the full control + +## Download +Go and get the latest release from the [release page](https://github.com/SommerEngineering/Sync/releases). Sync use the [SFTP library](https://github.com/pkg/sftp) from Dave Cheney. Thanks very much for the good work! diff --git a/ReadFlags.go b/ReadFlags.go index 6904791..570447e 100644 --- a/ReadFlags.go +++ b/ReadFlags.go @@ -1,16 +1,16 @@ -package main - -import ( - "flag" -) - -func readFlags() { - flag.StringVar(&serverAddrString, `server`, `127.0.0.1:22`, `The (remote) SSH server, e.g. 'my.host.com', 'my.host.com:22', '127.0.0.1:22', 'localhost:22'.`) - flag.StringVar(&username, `user`, `username`, `The user's name for the SSH server.`) - flag.StringVar(&password, `pwd`, ``, `The user's password for the SSH server. You can omit these argument. Thus, the program asks for the password on demand.`) - flag.StringVar(&localDir, `localDir`, `.`, `The local directory which should be synced. Use . for the current working directory.`) - flag.StringVar(&remoteDir, `remoteDir`, ``, `The remote directory which should be synced.`) - flag.BoolVar(&supervised, `supervised`, true, `Use the supervised mode? The algorithm asks you before any change.`) - flag.BoolVar(&pushOnly, `pushOnly`, true, `Use the push only mode, i.e. backup mode. Ignore any change on the remote side!`) - flag.Parse() -} +package main + +import ( + "flag" +) + +func readFlags() { + flag.StringVar(&serverAddrString, `server`, `127.0.0.1:22`, `The (remote) SSH server, e.g. 'my.host.com', 'my.host.com:22', '127.0.0.1:22', 'localhost:22'.`) + flag.StringVar(&username, `user`, `username`, `The user's name for the SSH server.`) + flag.StringVar(&password, `pwd`, ``, `The user's password for the SSH server. You can omit these argument. Thus, the program asks for the password on demand.`) + flag.StringVar(&localDir, `localDir`, `.`, `The local directory which should be synced. Use . for the current working directory.`) + flag.StringVar(&remoteDir, `remoteDir`, ``, `The remote directory which should be synced.`) + flag.BoolVar(&supervised, `supervised`, true, `Use the supervised mode? The algorithm asks you before any change.`) + flag.BoolVar(&pushOnly, `pushOnly`, true, `Use the push only mode, i.e. backup mode. Ignore any change on the remote side!`) + flag.Parse() +} diff --git a/Sync/ComparePath.go b/Sync/ComparePath.go index babc0f7..64fbd31 100644 --- a/Sync/ComparePath.go +++ b/Sync/ComparePath.go @@ -1,7 +1,7 @@ -package Sync - -func comparePath(localBase, localPath, remoteBase, remotePath string) bool { - localCompare := normalisePath(localBase, localPath) - remoteCompare := normalisePath(remoteBase, remotePath) - return localCompare == remoteCompare -} +package Sync + +func comparePath(localBase, localPath, remoteBase, remotePath string) bool { + localCompare := normalisePath(localBase, localPath) + remoteCompare := normalisePath(remoteBase, remotePath) + return localCompare == remoteCompare +} diff --git a/Sync/ConnectSSH.go b/Sync/ConnectSSH.go index 44c7bab..ca484bb 100644 --- a/Sync/ConnectSSH.go +++ b/Sync/ConnectSSH.go @@ -1,42 +1,42 @@ -package Sync - -import ( - "golang.org/x/crypto/ssh" - "log" - "time" -) - -func ConnectSSH(config *ssh.ClientConfig, serverAddrString string) (sshClientConnection *ssh.Client) { - - currentRetriesServer := 0 - - // Loop for retries: - for { - - // Try to connect to the SSH server: - if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil { - - // Failed: - currentRetriesServer++ - log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error()) - - // Is a retry alowed? - if currentRetriesServer < maxRetriesServer { - log.Println(`Retry...`) - time.Sleep(1 * time.Second) - } else { - - // After the return, this thread is closed down. The client can try it again... - log.Println(`No more retries for connecting the SSH server.`) - return - } - - } else { - - // Success: - log.Println(`Connected to the SSH server ` + serverAddrString) - sshClientConnection = sshClientConn - return - } - } -} +package Sync + +import ( + "golang.org/x/crypto/ssh" + "log" + "time" +) + +func ConnectSSH(config *ssh.ClientConfig, serverAddrString string) (sshClientConnection *ssh.Client) { + + currentRetriesServer := 0 + + // Loop for retries: + for { + + // Try to connect to the SSH server: + if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil { + + // Failed: + currentRetriesServer++ + log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error()) + + // Is a retry alowed? + if currentRetriesServer < maxRetriesServer { + log.Println(`Retry...`) + time.Sleep(1 * time.Second) + } else { + + // After the return, this thread is closed down. The client can try it again... + log.Println(`No more retries for connecting the SSH server.`) + return + } + + } else { + + // Success: + log.Println(`Connected to the SSH server ` + serverAddrString) + sshClientConnection = sshClientConn + return + } + } +} diff --git a/Sync/Constants.go b/Sync/Constants.go index 8219c7e..9b268fb 100644 --- a/Sync/Constants.go +++ b/Sync/Constants.go @@ -1,5 +1,5 @@ -package Sync - -const ( - maxRetriesServer = 16 // How many retries are allowed to create the SSH server's connection? -) +package Sync + +const ( + maxRetriesServer = 16 // How many retries are allowed to create the SSH server's connection? +) diff --git a/Sync/KeyboardInteractiveChallenge.go b/Sync/KeyboardInteractiveChallenge.go index c59f988..71aa88c 100644 --- a/Sync/KeyboardInteractiveChallenge.go +++ b/Sync/KeyboardInteractiveChallenge.go @@ -1,35 +1,35 @@ -package Sync - -import ( - "log" -) - -// Another auth. method. -func KeyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { - - // Log all the provided data: - log.Println(`User: ` + user) - log.Println(`Instruction: ` + instruction) - log.Println(`Questions:`) - for q := range questions { - log.Println(q) - } - - // How many questions are asked? - countQuestions := len(questions) - - if countQuestions == 1 { - - // We expect that in this case (only one question is asked), that the server want to know the password ;-) - answers = make([]string, countQuestions, countQuestions) - answers[0] = callbackPassword - - } else if countQuestions > 1 { - - // After logging, this call will exit the whole program: - log.Fatalln(`The SSH server is asking multiple questions! This program cannot handle this case.`) - } - - err = nil - return -} +package Sync + +import ( + "log" +) + +// Another auth. method. +func KeyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { + + // Log all the provided data: + log.Println(`User: ` + user) + log.Println(`Instruction: ` + instruction) + log.Println(`Questions:`) + for q := range questions { + log.Println(q) + } + + // How many questions are asked? + countQuestions := len(questions) + + if countQuestions == 1 { + + // We expect that in this case (only one question is asked), that the server want to know the password ;-) + answers = make([]string, countQuestions, countQuestions) + answers[0] = callbackPassword + + } else if countQuestions > 1 { + + // After logging, this call will exit the whole program: + log.Fatalln(`The SSH server is asking multiple questions! This program cannot handle this case.`) + } + + err = nil + return +} diff --git a/Sync/NormalisePath.go b/Sync/NormalisePath.go index 6c21541..18b5f5a 100644 --- a/Sync/NormalisePath.go +++ b/Sync/NormalisePath.go @@ -1,12 +1,12 @@ -package Sync - -import ( - "path/filepath" - "strings" -) - -func normalisePath(base, path string) string { - result := strings.Replace(path, base, ``, 1) - result = filepath.ToSlash(result) - return strings.ToLower(result) -} +package Sync + +import ( + "path/filepath" + "strings" +) + +func normalisePath(base, path string) string { + result := strings.Replace(path, base, ``, 1) + result = filepath.ToSlash(result) + return strings.ToLower(result) +} diff --git a/Sync/PasswordCallback.go b/Sync/PasswordCallback.go index 502a2d0..0a23632 100644 --- a/Sync/PasswordCallback.go +++ b/Sync/PasswordCallback.go @@ -1,10 +1,10 @@ -package Sync - -// Just a callback function for the password request. -func PasswordCallback() (string, error) { - return callbackPassword, nil -} - -func SetPassword4Callback(password string) { - callbackPassword = password -} +package Sync + +// Just a callback function for the password request. +func PasswordCallback() (string, error) { + return callbackPassword, nil +} + +func SetPassword4Callback(password string) { + callbackPassword = password +} diff --git a/Sync/ReadYesNoAnswer.go b/Sync/ReadYesNoAnswer.go index d4f3279..d9db4bc 100644 --- a/Sync/ReadYesNoAnswer.go +++ b/Sync/ReadYesNoAnswer.go @@ -1,23 +1,23 @@ -package Sync - -import ( - "fmt" - "strings" -) - -func readYesNoAnswer(defaultAnswer bool) bool { // true := yes - answer := `` - if _, scanError := fmt.Scan(&answer); scanError != nil { - return defaultAnswer - } - - if answer == `` || answer == ` ` { - return defaultAnswer - } - - if strings.ToLower(answer) == `y` { - return true - } - - return false -} +package Sync + +import ( + "fmt" + "strings" +) + +func readYesNoAnswer(defaultAnswer bool) bool { // true := yes + answer := `` + if _, scanError := fmt.Scan(&answer); scanError != nil { + return defaultAnswer + } + + if answer == `` || answer == ` ` { + return defaultAnswer + } + + if strings.ToLower(answer) == `y` { + return true + } + + return false +} diff --git a/Sync/Variables.go b/Sync/Variables.go index 13bee70..b14a9b5 100644 --- a/Sync/Variables.go +++ b/Sync/Variables.go @@ -1,15 +1,15 @@ -package Sync - -import ( - "os" -) - -var ( - callbackPassword = `` - localFiles = make(map[string]os.FileInfo) // The local files with local separator - localFilesNormalised = make(map[string]os.FileInfo) // The local files with std separator i.e. / and removed base dir - normalised2localFiles = make(map[string]string) // Mapping from normalised local file to local file - remoteFiles = make(map[string]os.FileInfo) // The remote files with remote separator - remoteFilesNormalised = make(map[string]os.FileInfo) // The remote files with std separator i.e. / and removed base dir - normalised2remoteFiles = make(map[string]string) // Mapping from normalised remote file to remote file -) +package Sync + +import ( + "os" +) + +var ( + callbackPassword = `` + localFiles = make(map[string]os.FileInfo) // The local files with local separator + localFilesNormalised = make(map[string]os.FileInfo) // The local files with std separator i.e. / and removed base dir + normalised2localFiles = make(map[string]string) // Mapping from normalised local file to local file + remoteFiles = make(map[string]os.FileInfo) // The remote files with remote separator + remoteFilesNormalised = make(map[string]os.FileInfo) // The remote files with std separator i.e. / and removed base dir + normalised2remoteFiles = make(map[string]string) // Mapping from normalised remote file to remote file +) diff --git a/Sync/WalkerLocal.go b/Sync/WalkerLocal.go index 823e924..988c6be 100644 --- a/Sync/WalkerLocal.go +++ b/Sync/WalkerLocal.go @@ -1,10 +1,10 @@ -package Sync - -import ( - "os" -) - -func walkerlocal(path string, info os.FileInfo, err error) error { - localFiles[path] = info - return nil -} +package Sync + +import ( + "os" +) + +func walkerlocal(path string, info os.FileInfo, err error) error { + localFiles[path] = info + return nil +} diff --git a/Variables.go b/Variables.go index 80f6b82..2a687a8 100644 --- a/Variables.go +++ b/Variables.go @@ -1,11 +1,11 @@ -package main - -var ( - username = `` // The SSH user's name - password = `` // The user's password - serverAddrString = `` // The SSH server address - localDir = `` // The local directory - remoteDir = `` // The remote directory - supervised = true // Should the tool work supervised? - pushOnly = true // Pushes only local changes to the remote i.e. backup mode -) +package main + +var ( + username = `` // The SSH user's name + password = `` // The user's password + serverAddrString = `` // The SSH server address + localDir = `` // The local directory + remoteDir = `` // The remote directory + supervised = true // Should the tool work supervised? + pushOnly = true // Pushes only local changes to the remote i.e. backup mode +)