Version 1.0.0
This commit is contained in:
parent
83f958f0d9
commit
cd58325ee3
58
Main.go
Normal file
58
Main.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/SommerEngineering/Sync/Sync"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Show the current version:
|
||||||
|
fmt.Println(`Sync v1.0.0`)
|
||||||
|
|
||||||
|
// Allow Go to use all CPUs:
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
// Read the configuration from the command-line args:
|
||||||
|
readFlags()
|
||||||
|
|
||||||
|
// Check if the directories are provided:
|
||||||
|
if localDir == `` || remoteDir == `` {
|
||||||
|
log.Println(`Please provide the local and remote directory.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the password was provided:
|
||||||
|
for true {
|
||||||
|
if password == `` {
|
||||||
|
// Promt for the password:
|
||||||
|
fmt.Println(`Please provide the password for the connection:`)
|
||||||
|
fmt.Scanln(&password)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the SSH configuration:
|
||||||
|
Sync.SetPassword4Callback(password)
|
||||||
|
config := &ssh.ClientConfig{
|
||||||
|
User: username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(password),
|
||||||
|
ssh.PasswordCallback(Sync.PasswordCallback),
|
||||||
|
ssh.KeyboardInteractive(Sync.KeyboardInteractiveChallenge),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the SSH server:
|
||||||
|
ssh := Sync.ConnectSSH(config, serverAddrString)
|
||||||
|
if ssh == nil {
|
||||||
|
log.Println(`It was not possible to connect to the SSH server.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer ssh.Close()
|
||||||
|
}
|
15
ReadFlags.go
Normal file
15
ReadFlags.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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.`)
|
||||||
|
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.Parse()
|
||||||
|
}
|
18
Sync/ComparePath.go
Normal file
18
Sync/ComparePath.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package Sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func comparePath(localBase, localPath, remoteBase, remotePath string) bool {
|
||||||
|
localCompare := normalisePath(localBase, localPath)
|
||||||
|
remoteCompare := normalisePath(remoteBase, remotePath)
|
||||||
|
return localCompare == remoteCompare
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalisePath(base, path string) string {
|
||||||
|
result := strings.Replace(path, base, ``, 1)
|
||||||
|
result = filepath.ToSlash(result)
|
||||||
|
return strings.ToLower(result)
|
||||||
|
}
|
42
Sync/Connect.go
Normal file
42
Sync/Connect.go
Normal file
@ -0,0 +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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
Sync/Constants.go
Normal file
5
Sync/Constants.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package Sync
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRetriesServer = 16 // How many retries are allowed to create the SSH server's connection?
|
||||||
|
)
|
35
Sync/KeyboardInteractiveChallenge.go
Normal file
35
Sync/KeyboardInteractiveChallenge.go
Normal file
@ -0,0 +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
|
||||||
|
}
|
10
Sync/PasswordCallback.go
Normal file
10
Sync/PasswordCallback.go
Normal file
@ -0,0 +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
|
||||||
|
}
|
48
Sync/Synchronise.go
Normal file
48
Sync/Synchronise.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package Sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/sftp"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Synchronise(ssh *ssh.Client, supervised bool, localDir, remoteDir string) {
|
||||||
|
/*
|
||||||
|
Algorithm:
|
||||||
|
- Get all local files and dirs
|
||||||
|
- Get all remote files and dirs
|
||||||
|
- Determine: ndl := newest mod time of all local files
|
||||||
|
- Determine: ndr := newest mod time of all remote files
|
||||||
|
- Free remote space
|
||||||
|
- Are remote files present, which are locally not present
|
||||||
|
and these remote files are older than ndl?
|
||||||
|
- Meaning: You have worked locally and remotely are older files which are not locally present.
|
||||||
|
Interpretation of change: You have deleted these files locally, because you own local
|
||||||
|
files which are newer that these files.
|
||||||
|
- delete these remote files! (may ask the user)
|
||||||
|
- Free local space
|
||||||
|
- Are local files present, which are remotely not present
|
||||||
|
and these local files are older than ndr?
|
||||||
|
- Meaning: You have worked remotely (or have synced with another PC), thus, local files are older
|
||||||
|
and these are not present remotely. Interpretation of change: You have delete these files
|
||||||
|
remotely, because your remotely files are newer that these files.
|
||||||
|
- delete these local files! (may ask the user)
|
||||||
|
- Download any new files
|
||||||
|
- Are remote files present, which are locally not present
|
||||||
|
and these remote files are newer than ndl?
|
||||||
|
- Meaning: These files are new to the local side
|
||||||
|
- Download these files! (may ask the user)
|
||||||
|
- Upload any new files
|
||||||
|
- Are local files present, which are remotely not present
|
||||||
|
and these local files are newer than ndr?
|
||||||
|
- Meaning: These files are new to the remote side
|
||||||
|
- Upload these files! (may ask the user)
|
||||||
|
- Changed remote files
|
||||||
|
- Are remote and local files present, where the remote file is newer?
|
||||||
|
- Meaning: These files are changed on the remote side
|
||||||
|
- Download these files and replace the local copies (may ask the user)
|
||||||
|
- Changed local files
|
||||||
|
- Are local and remote files present, where the local file is newer?
|
||||||
|
- Meaning: These files are changed on the local side
|
||||||
|
- Upload these files and replace the remote copies (may ask the user)
|
||||||
|
*/
|
||||||
|
}
|
5
Sync/Variables.go
Normal file
5
Sync/Variables.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package Sync
|
||||||
|
|
||||||
|
var (
|
||||||
|
callbackPassword = ``
|
||||||
|
)
|
10
Variables.go
Normal file
10
Variables.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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?
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user