From 5244f8741159c2188348afe9308b1778effa7dac Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Wed, 1 Jan 2025 22:18:08 +0100 Subject: [PATCH] Restored progress --- .vscode/tasks.json | 22 +++++ cmd/client/main.go | 44 ++++++++++ cmd/server/main.go | 1 + go.mod | 26 ++++++ go.sum | 54 ++++++++++++ internal/client/client.go | 42 ++++++++++ internal/client/setup.go | 104 +++++++++++++++++++++++ internal/core/config.go | 63 ++++++++++++++ internal/core/models.go | 25 ++++++ internal/core/utils.go | 8 ++ internal/server/server.go | 83 ++++++++++++++++++ internal/server/setup.go | 1 + lab/TODO.md | 74 ++++++++++++++++ lab/TcpServExample.gone | 91 ++++++++++++++++++++ lab/algo.txt | 24 ++++++ lab/core.gone | 116 +++++++++++++++++++++++++ lab/main.gone | 85 +++++++++++++++++++ lab/old client-main.gone | 172 ++++++++++++++++++++++++++++++++++++++ tests/tests.go | 26 ++++++ 19 files changed, 1061 insertions(+) create mode 100644 .vscode/tasks.json create mode 100644 cmd/client/main.go create mode 100644 cmd/server/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/client/client.go create mode 100644 internal/client/setup.go create mode 100644 internal/core/config.go create mode 100644 internal/core/models.go create mode 100644 internal/core/utils.go create mode 100644 internal/server/server.go create mode 100644 internal/server/setup.go create mode 100644 lab/TODO.md create mode 100644 lab/TcpServExample.gone create mode 100644 lab/algo.txt create mode 100644 lab/core.gone create mode 100644 lab/main.gone create mode 100644 lab/old client-main.gone create mode 100644 tests/tests.go diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f230f79 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,22 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Go run file", + "type": "shell", + "command": "go run ${file}", + "args": [], + "group": "none", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + } + ] +} \ No newline at end of file diff --git a/cmd/client/main.go b/cmd/client/main.go new file mode 100644 index 0000000..be04c43 --- /dev/null +++ b/cmd/client/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "cosync/internal/client" + "cosync/internal/server" + "cosync/tests" + "os" +) + +//TODO: handle arguments correctly +func main() { + argc := len(os.Args) + if argc < 2 { + panic("Not enough arguments (try --help)") + } + + + for i := 1; i < argc; i++ { + + switch (os.Args[i]) { + + case "-s": + server.Run() + // break + + case "-c": + client.Run() + // break + + case "-p": + panic("Not implemented") + + case "--help": + panic("Not implemented") + + case "--test": + tests.TestAll() + + default: + panic("Bad argument : \"" + os.Args[i] + "\"") + + } + } +} \ No newline at end of file diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..f275077 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1 @@ +package server \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..05c0b3a --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module cosync + +go 1.22.3 + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2b668ad --- /dev/null +++ b/go.sum @@ -0,0 +1,54 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..c8a3480 --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,42 @@ +package client + + +import ( + "log" + "cosync/internal/core" + "net" + "strconv" +) + +//TODO: handle errors, review logging +func Run() { + + log.Println("Loading config") + + // Loading config into program + err := core.LoadConfig() + if err != nil { + log.Fatal(err) + panic(err) + } + config := core.GetConfig() + + log.Println("Loaded") + + serverAddress := config["server"].(string) + serverPort := strconv.Itoa(config["port"].(int)) + + log.Println("Connecting to server") + + // Connect to the server + conn, err := net.Dial("tcp", serverAddress + ":" + serverPort) + if err != nil { + log.Fatal(err) + panic(err) + } + defer conn.Close() + + log.Println("Connected") + + conn.Write([]byte("Hello server")) +} \ No newline at end of file diff --git a/internal/client/setup.go b/internal/client/setup.go new file mode 100644 index 0000000..72d3be9 --- /dev/null +++ b/internal/client/setup.go @@ -0,0 +1,104 @@ +package client + +import ( + "cosync/internal/core" +) + +const ( + colorRed = "\033[0;31m" + colorGreen = "\033[1;32m" + colorNone = "\033[0m" + separator = "\n ============================================== \n\n" +) + +func Setup() { + + // Print logo + println(" ______ ______ \n .' ___ | .' ____ \\ \n/ .' \\_| .--. | (___ \\_| _ __ _ .--. .---. \n| | / .'`\\ \\ _.____`. [ \\ [ ][ `.-. | / /'`\\] \n\\ `.___.'\\| \\__. || \\____) | \\ '/ / | | | | | \\__. \n `.____ .' '.__.' \\______.'[\\_: / [___||__]'.___.' \n \\__.' ") + print(separator) + + // Greeting + println("Hi, welcome to cosync, let's set it up") + println("Don't hesitate to give a look at the doc to know how it works ! :) \n -> https://docs.oblic-parallels.fr/cosync\n") + + // Try create config files + err := core.MkConfig() + if err != nil { + println(colorRed + "Setup: Failed to create config files" + colorNone) + panic(err) + } + + // Prompt for server/port assignation + print("Server address: ") + // ... + print("Port") + // ... + + // Update config file accordingly + // ... + println(colorGreen + "Config has been updated with new values" + colorNone) + + // Prompt to start service + print("Do you want to start CoSync now ? [Y/n]: ") + //... +} + +//! This code no longer needs to exist as config is now handled by cosync/config +// Then it will be removed soon + +// func mkConfigFiles(configFolderPath string, configFileName string, syncFileName string) { + +// // Check if folder already exists +// _, err := os.Stat(configFolderPath) +// if err != nil && os.IsNotExist(err) { +// // Create config folder +// err = os.Mkdir(configFolderPath, os.ModePerm) +// if err != nil { +// println(colorRed + "\n\nCouldn't create the config folder" + colorNone) +// panic("Setup: Couldn't create " + configFolderPath + " -> " + err.Error()) +// } +// } + +// // Check if config file exists +// configFilePath := configFolderPath + configFileName +// _, err = os.Stat(configFilePath) +// if err != nil && os.IsNotExist(err) { +// // Create config file +// _, err = os.Create(configFilePath) +// if err != nil { +// println(colorRed + "\n\nCouldn't create the config file" + colorNone) +// panic("Setup: Couldn't create " + configFilePath + " -> " + err.Error()) +// } +// } + +// syncFilePath := configFolderPath + syncFileName +// _, err = os.Stat(syncFilePath) +// if err != nil && os.IsNotExist(err) { +// // Create config folder +// _, err = os.Create(syncFilePath) +// if err != nil { +// println(colorRed + "\n\nCouldn't create the sync file" + colorNone) +// panic("Setup: Couldn't create " + syncFilePath + " -> " + err.Error()) +// } +// } + +// } + +// func WriteDefaultConfig(configFilePath string) { + +// // Open file +// f, err := os.Open(configFilePath) +// if err != nil { +// println(colorRed + "\n\nCouldn't open the config file" + colorNone) +// panic("Setup: Couldn't access " + configFilePath + " -> " + err.Error()) +// } +// defer f.Close() + +// // Write file +// // TODO: import pre existing config file +// _, err = f.WriteString("{}") +// if err != nil { +// println(colorRed + "\n\nCouldn't open the config file" + colorNone) +// panic("Setup: Couldn't access " + configFilePath + " -> " + err.Error()) +// } +// } diff --git a/internal/core/config.go b/internal/core/config.go new file mode 100644 index 0000000..4166fc2 --- /dev/null +++ b/internal/core/config.go @@ -0,0 +1,63 @@ +package core + +import ( + "errors" + "os/user" + + "github.com/spf13/viper" +) + + +// Config will follow this model +// server : string +// port : int + + + + + +// Load config file +func LoadConfig() error { + + // Get current user + currentUser, err := user.Current() + if err != nil { + // panic("LoadConfig: Failed to retrieve curent user -> " + err.Error()) + return err + } + + // Set default values + viper.SetDefault("server", "example.com") + viper.SetDefault("port", 5660) + + // Set config paths + viper.SetConfigName("config") + viper.SetConfigType("json") + viper.AddConfigPath("/etc/cosync/") + viper.AddConfigPath("/home/" + currentUser.Username + "/.config/cosync") + + // Read from config files + err = viper.ReadInConfig() + if err != nil { + panic("LoadConfig: Failed to read config file -> " + err.Error()) + // return err + } + + return nil +} + +// Generate config folder and files +func MkConfig() error { + return errors.New("not implemented") +} + +// Load sync list from sync file +func LoadSyncList() error { + return errors.New("not implemented") +} + +// returns all config variables +// ! WARNING: Do not use this function without executing LoadConfig prior +func GetConfig() map[string]any { + return viper.AllSettings() +} diff --git a/internal/core/models.go b/internal/core/models.go new file mode 100644 index 0000000..3a18d09 --- /dev/null +++ b/internal/core/models.go @@ -0,0 +1,25 @@ +package core + +import ( + "time" +) + +type File struct { + ID string + ParentDirectory string + Metadata FileMetadata + State SynchronizationState +} + +type FileMetadata struct { + Name string + Size int64 + ModifiedTime time.Time + Hash string // For comparing file content +} + +type SynchronizationState struct { + Status string // "pending", "in_progress", "completed", "error" + Progress float64 + LastError error +} \ No newline at end of file diff --git a/internal/core/utils.go b/internal/core/utils.go new file mode 100644 index 0000000..ebaad97 --- /dev/null +++ b/internal/core/utils.go @@ -0,0 +1,8 @@ +package core + + +//! Not implemented +func CompareFiles(file1 File, file2 File) bool { + return false +} + diff --git a/internal/server/server.go b/internal/server/server.go new file mode 100644 index 0000000..7a5be5a --- /dev/null +++ b/internal/server/server.go @@ -0,0 +1,83 @@ +package server + +import ( + "bufio" + "cosync/internal/core" + "io" + "log" + "net" + "strconv" +) + +// TODO: handle errors, review logging +func Run() { + + log.Println("Loading config") + + // Loading config into program + err := core.LoadConfig() + if err != nil { + log.Fatal(err) + panic(err) + } + config := core.GetConfig() + + log.Println("Loaded") + + serverPort := strconv.Itoa(config["port"].(int)) + + // Listening + listener, err := net.Listen("tcp", ":" + serverPort) + if err != nil { + panic(err) + } + defer listener.Close() + + log.Println("Server listening on port " + serverPort) + + for { + conn, err := listener.Accept() + if err != nil { + log.Println("Error accepting connection:", err) + continue + } + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + + // tells the connector to close once exchange ends + defer conn.Close() + + clientAddress := conn.RemoteAddr().String() + log.Println("Accepting from ", clientAddress) + + // Read the message sent by the client (line by line) + reader := bufio.NewReader(conn) + for { + message, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + log.Println(clientAddress, " disconnected") + return + } + log.Println("Error reading from ", clientAddress, ": ", err) + return + } + + // to rm + println(message) + // fmt.Println("Received from client:", message) + + // Process the message (e.g., send a response) + // ... + + // Send a response to the client + _, err = conn.Write([]byte("Server received your message\n")) + if err != nil { + log.Println("Error replying to", clientAddress,": ", err) + return + } + } +} diff --git a/internal/server/setup.go b/internal/server/setup.go new file mode 100644 index 0000000..f275077 --- /dev/null +++ b/internal/server/setup.go @@ -0,0 +1 @@ +package server \ No newline at end of file diff --git a/lab/TODO.md b/lab/TODO.md new file mode 100644 index 0000000..fb7cc97 --- /dev/null +++ b/lab/TODO.md @@ -0,0 +1,74 @@ +- Implement a core package that will be in charge of file comparison, transfer + +- Implement protocol : + - Be careful about metadata, large files, error handling and performance + - Implement the sql version + - Implement the tls version + +- Create configuration file : + - server address + - sync mode + - sync interval + +- Enhanced error handling : + - Only use panic during uncoverable situations + - Use more explicit error types + +- Use json/yaml config format (see viper) + +- Authentication : + - Use a token based authentication system + - Use a secure way to store the token (KMS ?) + +- Maintain a data structure to track the synchronization state of files : + - Local status + - Remote status + - Synchronization status + +- See io/ioutil or io/fs to read/write files + +- Break functions into little functions + +- Use appropriate data structures to represent files and folders. Consider using a map to store file information for faster lookups. + +- Improve logging (using a library like logrus or zap) ? + +- Unit & integration tests + +- Improve code style + +- Document + + + + + + +// Gemini + +- Implement a CLI : + - Use a library like cobra or urfave/cli + +- Implement a GUI : + - Use a library like fltk or qt + +- Implement a web interface : + - Use a library like gin or echo + +- Implement a daemon : + - Use a library like daemon or godo + +- Implement a service : + - Use a library like consul or etcd + +- Implement a cloud storage integration : + - Use a library like aws-sdk-go or google-cloud-go + +- Implement a version control system integration : + - Use a library like git or mercurial + +- Implement a backup system integration : + - Use a library like restic or borg + +- Implement a file system watcher : + - Use a library like fsnotify or inotify \ No newline at end of file diff --git a/lab/TcpServExample.gone b/lab/TcpServExample.gone new file mode 100644 index 0000000..0be5ee7 --- /dev/null +++ b/lab/TcpServExample.gone @@ -0,0 +1,91 @@ +package lab + +import ( + "bufio" + "fmt" + "io" + "net" + "os" +) + +func ExampleMain() { + // Choose either server or client mode + if len(os.Args) > 1 && os.Args[1] == "server" { + runServer() + } else { + runClient() + } +} + +func runServer() { + listener, err := net.Listen("tcp", ":8080") + if err != nil { + panic(err) + } + defer listener.Close() + + fmt.Println("Server listening on :8080") + + for { + conn, err := listener.Accept() + if err != nil { + fmt.Println("Error accepting connection:", err) + continue + } + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + defer conn.Close() + + reader := bufio.NewReader(conn) + for { + message, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + fmt.Println("Client disconnected") + return + } + fmt.Println("Error reading from client:", err) + return + } + fmt.Println("Received from client:", message) + + // Process the message (e.g., send a response) + // ... + + // Send a response to the client + _, err = conn.Write([]byte("Server received your message\n")) + if err != nil { + fmt.Println("Error writing to client:", err) + return + } + } +} + +func runClient() { + conn, err := net.Dial("tcp", "localhost:8080") + if err != nil { + panic(err) + } + defer conn.Close() + + fmt.Println("Connected to server") + + // Send a message to the server + _, err = conn.Write([]byte("Hello from the client!\n")) + if err != nil { + fmt.Println("Error writing to server:", err) + return + } + + // Read the server's response + reader := bufio.NewReader(conn) + message, err := reader.ReadString('\n') + if err != nil { + fmt.Println("Error reading from server:", err) + return + } + fmt.Println("Received from server:", message) +} diff --git a/lab/algo.txt b/lab/algo.txt new file mode 100644 index 0000000..2f3f755 --- /dev/null +++ b/lab/algo.txt @@ -0,0 +1,24 @@ +1. [On first setup] configure what files must be saved and their ID, configure sync mode you want to use (reflect up, reflect down, or bidirectionnal) +2. authenticate to the serv +3. [On first setup] Choose if you want to download or upload existing files +4. // index the files +5. // Check if changes were made, if not, do nothing +6. // Encode the files +7. Transfer the files over tcp with ssl +8. // Decode the files +9. // [On server] [if there are conflicts] take the last modified file +10. Reupdate every hour + + + + + +Authentication: + Register: + - client sends registration request to the server + - server returns fail/success code and a token + - client stores the token locally then authenticate + + Authenticate: + - client sends token + - server returns bool isTokenValid and link client to the token temporarly \ No newline at end of file diff --git a/lab/core.gone b/lab/core.gone new file mode 100644 index 0000000..ed3f980 --- /dev/null +++ b/lab/core.gone @@ -0,0 +1,116 @@ +// core/sync.go +package lab + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" +) + +// File represents file metadata +type File struct { + Name string + Path string + Size int64 + Checksum string + ModTime time.Time +} + +// Sync compares local and remote files and performs synchronization +func Sync(localRoot, remoteRoot string) error { + // 1. Scan local files + localFiles, err := scanFiles(localRoot) + if err != nil { + return fmt.Errorf("error scanning local files: %w", err) + } + + // 2. Scan remote files (replace with actual server communication) + remoteFiles, err := scanFiles(remoteRoot) + if err != nil { + return fmt.Errorf("error scanning remote files: %w", err) + } + + // 3. Compare and synchronize + for _, localFile := range localFiles { + // Check if file exists remotely + remoteFile, found := remoteFiles[localFile.Path] + if !found { + // Upload file + err := uploadFile(localFile.Path, remoteRoot) + if err != nil { + return fmt.Errorf("error uploading file: %w", err) + } + } else { + // Compare timestamps + if localFile.ModTime.After(remoteFile.ModTime) { + // Upload updated file + err := uploadFile(localFile.Path, remoteRoot) + if err != nil { + return fmt.Errorf("error uploading file: %w", err) + } + } + } + } + + // 4. Delete files that are no longer present locally + for _, remoteFile := range remoteFiles { + if _, found := localFiles[remoteFile.Path]; !found { + // Delete file remotely + err := deleteFile(remoteFile.Path, remoteRoot) + if err != nil { + return fmt.Errorf("error deleting file: %w", err) + } + } + } + + return nil +} + +// scanFiles recursively scans a directory and returns a map of file metadata +func scanFiles(root string) (map[string]*File, error) { + files := make(map[string]*File) + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + checksum := calculateChecksum(data) + files[path] = &File{ + Name: info.Name(), + Path: path, + Size: info.Size(), + Checksum: checksum, + ModTime: info.ModTime(), + } + } + return nil + }) + return files, err +} + +// uploadFile uploads a file to the remote server +func uploadFile(localPath, remoteRoot string) error { + // Implement file upload logic + // ... + return nil +} + +// deleteFile deletes a file from the remote server +func deleteFile(remotePath, remoteRoot string) error { + // Implement file deletion logic + // ... + return nil +} + +// calculateChecksum calculates the checksum of a file +func calculateChecksum(data []byte) string { + // Implement checksum calculation logic + // ... + return "" +} diff --git a/lab/main.gone b/lab/main.gone new file mode 100644 index 0000000..490fece --- /dev/null +++ b/lab/main.gone @@ -0,0 +1,85 @@ +package lab + +import ( + "io" + "net" + // "os" + "time" + // "sync" +) + +func NotMain() { + + TestServer() + // f, err := os.Open("/home/shared/Workplan/CoSync/lab/") + // if err != nil { + // panic(err) + // } + // files, err := f.Readdir(0) + // if err != nil { + // panic(err) + // } + + // for _, v := range files { + // println(v.Name(), v.IsDir()) + // } +} + +func Server() { + + listener, err := net.Listen("tcp", "localhost:4945") + if err != nil { + panic(err) + } + defer listener.Close() + + conn, err := listener.Accept() + if err != nil { + panic(err) + } + defer conn.Close() + + msg, err := io.ReadAll(conn) + if err != nil { + panic(err) + } + + conn.Write([]byte("hello from server")) + io.Closer.Close(conn) + + print("[SERVER] ", conn.RemoteAddr().String(), " : ") + println(string(msg)) + +} + +func Client(message string) { + srv, err := net.Dial("tcp", "localhost:4945") + if err != nil { + panic(err) + } + defer srv.Close() + + // format message + // msg := make([]byte, 0) + // msg = append(msg, []byte(message)...) + msg2 := []byte(message) + + srv.Write(msg2) + println("[Client] Wrote msg") + + //TODO: reprise + var b []byte + b2, err :=srv.Read(b) + if err != nil { + panic(err) + } + println("[Client] Read msg : " + string(b2)) +} + +func TestServer() { + go Server() + go Client("this is a test") + + time.Sleep(3 * time.Second) + println("[Main] finished") +} diff --git a/lab/old client-main.gone b/lab/old client-main.gone new file mode 100644 index 0000000..c4ae4e9 --- /dev/null +++ b/lab/old client-main.gone @@ -0,0 +1,172 @@ +package lab + + +import ( + "bufio" + "os/user" + "time" + + // "fmt" + // "io"q + "net/http" + "os" + "strings" + "cosync/internal/client" +) + +const ( + colorRed = "\033[0;31m" + colorGreen = "\033[1;32m" + colorNone = "\033[0m" + separator = "\n ============================================== \n\n" +) + +var ( + currentUser, _ = user.Current() + configFolder = "/home/" + currentUser.Username + "/.config/cosync/" + configFilePath = configFolder + "files.sync" + server = "https://sync.oblic-parallels.fr" +) + +type file struct { + path string + modTime time.Time +} + +func main() { + + // Print logo + println(" ______ ______ \n .' ___ | .' ____ \\ \n/ .' \\_| .--. | (___ \\_| _ __ _ .--. .---. \n| | / .'`\\ \\ _.____`. [ \\ [ ][ `.-. | / /'`\\] \n\\ `.___.'\\| \\__. || \\____) | \\ '/ / | | | | | \\__. \n `.____ .' '.__.' \\______.'[\\_: / [___||__]'.___.' \n \\__.' ") + + // Open config file or create it if doesn't exists + configFile, err := os.Open(configFilePath) + if err == nil { + client.Setup() + } + defer configFile.Close() + + // Get status from the server + resp, err := http.Get(server) + if err != nil { + print("\n\n" + colorRed) + panic(err) + } + defer resp.Body.Close() + + // Print server status + print(separator) + println("using server: ", server) + println("Response status:", resp.Status) + + // Authenticate + // I'll do it later + + print(separator) + println("Checking files :") + + // Check if config file isn't empty + configFileStats, _ := configFile.Stat() + if configFileStats.Size() == 0 { + println("\n No files listed in config. There is nothing to do.") + println("\n\n > Exiting ...") + return + } + + // Check and index all lines from config + + configFileScanner := bufio.NewScanner(configFile) + register := make(map[string]file) + + for configFileScanner.Scan() { + line := configFileScanner.Text() + + print("\n ", line) + + if line != "" { + index(line, register) + print(colorGreen + " ✔" + colorNone) + } + } + println() + + // Read from server + // scanner := bufio.NewScanner(resp.Body) + // for scanner.Scan() { + // println(scanner.Text()) + // } + + // if err := scanner.Err(); err != nil { + // panic(err) + // } + + //test + // f, err := os.Open("/home/guillem/.vscode/extensions/extensions.json") + // if err != nil { + // panic(err) + // } + // defer f.Close() + + // fs, _ := f.Stat() + // if fs.ModTime().Before() + +} + +func index(line string, register map[string]file) { + + decomposedLine := strings.Split(line, " : ") + + // check separator + if len(decomposedLine) != 2 { + println(colorRed + " ✘\n\nThis line is not valid" + colorNone) + panic(line + " is not a valid syntax") + } + + // Check if id is valid + if !IsValidID(decomposedLine[0]) { + println(colorRed + " ✘\n\nThis line is not valid" + colorNone) + panic("Id must be composed only by alphanumerical characters") + } + + appendFiles(decomposedLine[0], decomposedLine[1], register) + +} + +func IsValidID(id string) bool { + return strings.ContainsFunc(id, IsSpecialRune) +} +func IsSpecialRune(r rune) bool { + return (r < 'A' || r > 'z' || (r > 'Z' && r < 'a')) && (r < '0' || r > '9') && r != '_' +} + +func appendFiles(id string, path string, register map[string]file) { + + // get subfiles and subfolders + f, err := os.Open(path) + if err != nil { + println(colorRed + " ✘\n\n" + colorNone) + panic(err) + } + defer f.Close() + + fstat, err := f.Stat() + if err != nil { + println(colorRed + " ✘\n\n" + colorNone) + panic(err) + } + + // if path points to a folder + if fstat.IsDir() { + files, err := f.ReadDir(0) + if err != nil { + println(colorRed + " ✘\n\n" + colorNone) + panic(err) + } + + // call this function recursively for all subfiles and folders + for i := 0; i < len(files); i++ { + appendFiles(id+"-"+files[i].Name(), path+"/"+files[i].Name(), register) + } + } else { + register[id] = file{path, fstat.ModTime()} + } +} diff --git a/tests/tests.go b/tests/tests.go new file mode 100644 index 0000000..df7e5a8 --- /dev/null +++ b/tests/tests.go @@ -0,0 +1,26 @@ +package tests + +import ( + // "testing" + "cosync/internal/client" + "cosync/internal/server" +) + +func TestAll() bool { + TestClientServerCommunication() + return false +} + +func TestClientServerCommunication() bool { + go client.Run() + go server.Run() + return false +} + +func TestConfigLoading() bool { + return false +} + +func TestConfigCreation() bool { + return false +} \ No newline at end of file