Golang Rewrite
This commit is contained in:
commit
561bf06826
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.idea/
|
||||||
|
opensend
|
||||||
|
opensend-arm
|
113
config.go
Normal file
113
config.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/pkg/browser"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create config type to store action type and data
|
||||||
|
type Config struct {
|
||||||
|
ActionType string
|
||||||
|
ActionData string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate and return a new Config struct
|
||||||
|
func NewConfig(actionType string, actionData string) *Config {
|
||||||
|
return &Config{ActionType: actionType, ActionData: actionData}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create config file
|
||||||
|
func (config *Config) CreateFile(dir string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Create config file at given directory
|
||||||
|
configFile, err := os.Create(dir + "/config.json")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating config file") }
|
||||||
|
// Close config file at the end of this function
|
||||||
|
defer configFile.Close()
|
||||||
|
// Marshal given Config struct into a []byte
|
||||||
|
jsonData, err := json.Marshal(config)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error encoding JSON") }
|
||||||
|
// Write []byte to previously created config file
|
||||||
|
bytesWritten, err := configFile.Write(jsonData)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing JSON to file") }
|
||||||
|
// Log bytes written
|
||||||
|
log.Info().Str("file", "config.json").Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all required files into given directory
|
||||||
|
func (config *Config) CollectFiles(dir string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// If action type is file
|
||||||
|
if config.ActionType == "file" {
|
||||||
|
// Open file path in config.ActionData
|
||||||
|
src, err := os.Open(config.ActionData)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error opening file from config") }
|
||||||
|
// Close source file at the end of this function
|
||||||
|
defer src.Close()
|
||||||
|
// Create new file with the same name at given directory
|
||||||
|
dst, err := os.Create(dir + "/" + filepath.Base(config.ActionData))
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Close new file at the end of this function
|
||||||
|
defer dst.Close()
|
||||||
|
// Copy data from source file to destination file
|
||||||
|
_, err = io.Copy(dst, src)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error copying data to file") }
|
||||||
|
// Replace file path in config.ActionData with file name
|
||||||
|
config.ActionData = filepath.Base(config.ActionData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read config file at given file path
|
||||||
|
func (config *Config) ReadFile(filePath string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Read file at filePath
|
||||||
|
fileData, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading config file") }
|
||||||
|
// Unmarshal data from JSON into config struct
|
||||||
|
err = json.Unmarshal(fileData, config)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error decoding JSON") }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute action specified in config
|
||||||
|
func (config *Config) ExecuteAction(srcDir string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// If action is file
|
||||||
|
if config.ActionType == "file" {
|
||||||
|
// Open file from config at given directory
|
||||||
|
src, err := os.Open(srcDir + "/" + config.ActionData)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading file from config") }
|
||||||
|
// Close source file at the end of this function
|
||||||
|
defer src.Close()
|
||||||
|
// Get user's home directory
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error getting home directory") }
|
||||||
|
// Create file in user's Downloads directory
|
||||||
|
dst, err := os.Create(homeDir + "/Downloads/" + config.ActionData)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Close destination file at the end of this function
|
||||||
|
defer dst.Close()
|
||||||
|
// Copy data from source file to destination file
|
||||||
|
_, err = io.Copy(dst, src)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error copying data to file") }
|
||||||
|
// If action is url
|
||||||
|
} else if config.ActionType == "url" {
|
||||||
|
// Attempt to open URL in browser
|
||||||
|
err := browser.OpenURL(config.ActionData)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error opening browser") }
|
||||||
|
// Catchall
|
||||||
|
} else {
|
||||||
|
// Log unknown action type
|
||||||
|
log.Fatal().Msg("Unknown action type " + config.ActionType)
|
||||||
|
}
|
||||||
|
}
|
59
deviceDiscovery.go
Normal file
59
deviceDiscovery.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/grandcat/zeroconf"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Discover opensend receivers on the network
|
||||||
|
func DiscoverReceivers() ([]string, []string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Create zeroconf resolver
|
||||||
|
resolver, err := zeroconf.NewResolver(nil)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating zeroconf resolver") }
|
||||||
|
// Create channel for zeroconf entries
|
||||||
|
entries := make(chan *zeroconf.ServiceEntry)
|
||||||
|
// Create slice to store hostnames of discovered receivers
|
||||||
|
var discoveredReceivers []string
|
||||||
|
// Create slice to store IPs of discovered receivers
|
||||||
|
var discoveredReceiverIPs []string
|
||||||
|
// Concurrently run mDNS query
|
||||||
|
go func(results <-chan *zeroconf.ServiceEntry) {
|
||||||
|
// For each entry
|
||||||
|
for entry := range results {
|
||||||
|
// Append hostname to discoveredReceivers
|
||||||
|
discoveredReceivers = append(discoveredReceivers, entry.HostName)
|
||||||
|
// Append IP to discoveredReceiverIPs
|
||||||
|
discoveredReceiverIPs = append(discoveredReceiverIPs, entry.AddrIPv4[0].String())
|
||||||
|
}
|
||||||
|
}(entries)
|
||||||
|
|
||||||
|
// Create context with 4 second timeout
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
|
||||||
|
// Cancel context at the end of this function
|
||||||
|
defer cancel()
|
||||||
|
// Browse for mDNS entries
|
||||||
|
err = resolver.Browse(ctx, "_opensend._tcp", "local.", entries)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error browsing zeroconf services") }
|
||||||
|
|
||||||
|
// Send Done signal to context
|
||||||
|
<-ctx.Done()
|
||||||
|
// Return discovered receiver slices
|
||||||
|
return discoveredReceivers, discoveredReceiverIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register opensend zeroconf service on the network
|
||||||
|
func RegisterService() func() {
|
||||||
|
// Get computer hostname
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
// Register zeroconf service {hostname}._opensend._tcp.local.
|
||||||
|
server, err := zeroconf.Register(hostname, "_opensend._tcp", "local.", 9797, []string{"txtv=0", "lo=1", "la=2"}, nil)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error registering zeroconf service") }
|
||||||
|
// Return server.Shutdown() function to allow for shutdown in main()
|
||||||
|
return server.Shutdown
|
||||||
|
}
|
127
fileCrypto.go
Normal file
127
fileCrypto.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encrypt given file using the shared key
|
||||||
|
func EncryptFile(filePath string, newFilePath string, sharedKey string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Read data from file
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
|
||||||
|
// Create md5 hash of password in order to make it the required size
|
||||||
|
md5Hash := md5.New()
|
||||||
|
md5Hash.Write([]byte(sharedKey))
|
||||||
|
// Encode md5 hash bytes into hexadecimal
|
||||||
|
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
|
||||||
|
// Create new AES cipher
|
||||||
|
block, _ := aes.NewCipher([]byte(hashedKey))
|
||||||
|
// Create GCM for AES cipher
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
|
||||||
|
// Make byte slice for nonce
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
// Read random bytes into nonce slice
|
||||||
|
_, err = io.ReadFull(rand.Reader, nonce)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating nonce") }
|
||||||
|
// Encrypt data
|
||||||
|
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||||
|
// Create new file
|
||||||
|
newFile, err := os.Create(newFilePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Defer file close
|
||||||
|
defer newFile.Close()
|
||||||
|
// Write ciphertext to new file
|
||||||
|
bytesWritten, err := newFile.Write(ciphertext)
|
||||||
|
// Log bytes written and to which file
|
||||||
|
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt given file using the shared key
|
||||||
|
func DecryptFile(filePath string, newFilePath string, sharedKey string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Read data from file
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
|
||||||
|
// Create md5 hash of password in order to make it the required size
|
||||||
|
md5Hash := md5.New()
|
||||||
|
md5Hash.Write([]byte(sharedKey))
|
||||||
|
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
|
||||||
|
// Create new AES cipher
|
||||||
|
block, _ := aes.NewCipher([]byte(hashedKey))
|
||||||
|
// Create GCM for AES cipher
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
|
||||||
|
// Get standard GCM nonce size
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
// Get nonce and ciphertext from data
|
||||||
|
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
|
||||||
|
// Decrypt data
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error decrypting data") }
|
||||||
|
// Create new file
|
||||||
|
newFile, err := os.Create(newFilePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Defer file close
|
||||||
|
defer newFile.Close()
|
||||||
|
// Write ciphertext to new file
|
||||||
|
bytesWritten, err := newFile.Write(plaintext)
|
||||||
|
// Log bytes written and to which file
|
||||||
|
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt files in given directory using shared key
|
||||||
|
func EncryptFiles(dir string, sharedKey string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Walk given directory
|
||||||
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
// If error reading, return err
|
||||||
|
if err != nil { return err }
|
||||||
|
// If file is not a directory and is not the key
|
||||||
|
if !info.IsDir() && !strings.Contains(path, "aesKey"){
|
||||||
|
// Encrypt the file using shared key, appending .enc
|
||||||
|
EncryptFile(path, path + ".enc", sharedKey)
|
||||||
|
// Remove unencrypted file
|
||||||
|
err := os.Remove(path)
|
||||||
|
if err != nil {return err}
|
||||||
|
}
|
||||||
|
// Return nil if no error occurs
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error encrypting files") }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt files in given directory using shared key
|
||||||
|
func DecryptFiles(dir string, sharedKey string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Walk given directory
|
||||||
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
// If error reading, return err
|
||||||
|
if err != nil { return err }
|
||||||
|
// If file is not a directory and is encrypted
|
||||||
|
if !info.IsDir() && strings.Contains(path, ".enc") {
|
||||||
|
// Decrypt the file using the shared key, removing .enc
|
||||||
|
DecryptFile(path, strings.TrimSuffix(path, ".enc"), sharedKey)
|
||||||
|
}
|
||||||
|
// Return nil if no errors occurred
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error decrypting files") }
|
||||||
|
}
|
166
files.go
Normal file
166
files.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Save encrypted key to file
|
||||||
|
func SaveEncryptedKey(encryptedKey []byte, filePath string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Create file at given file path
|
||||||
|
keyFile, err := os.Create(filePath)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Close file at the end of this function
|
||||||
|
defer keyFile.Close()
|
||||||
|
// Write encrypted key to file
|
||||||
|
bytesWritten, err := keyFile.Write(encryptedKey)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing key to file") }
|
||||||
|
// Log bytes written
|
||||||
|
log.Info().Str("file", filepath.Base(filePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create HTTP server to transmit files
|
||||||
|
func SendFiles(dir string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Instantiate http.Server struct
|
||||||
|
srv := &http.Server{}
|
||||||
|
// Listen on all ipv4 addresses on port 9898
|
||||||
|
listener, err := net.Listen("tcp4", ":9898")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
|
||||||
|
|
||||||
|
// If client connects to /:filePath
|
||||||
|
http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
// Set file to first path components of URL, excluding first /
|
||||||
|
file := req.URL.Path[1:]
|
||||||
|
// Read file at specified location
|
||||||
|
fileData, err := ioutil.ReadFile(dir + "/" + file)
|
||||||
|
// If there was an error reading
|
||||||
|
if err != nil {
|
||||||
|
// Warn user of error
|
||||||
|
log.Warn().Err(err).Msg("Error reading file")
|
||||||
|
// Otherwise
|
||||||
|
} else {
|
||||||
|
// Inform user client has requested a file
|
||||||
|
log.Info().Str("file", file).Msg("GET File")
|
||||||
|
}
|
||||||
|
// Write file to ResponseWriter
|
||||||
|
_, err = fmt.Fprint(res, string(fileData))
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||||
|
})
|
||||||
|
|
||||||
|
// If client connects to /index
|
||||||
|
http.HandleFunc("/index", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
// Inform user a client has requested the file index
|
||||||
|
log.Info().Msg("GET Index")
|
||||||
|
// Get directory listing
|
||||||
|
dirListing, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading directory") }
|
||||||
|
// Create new slice to house filenames for index
|
||||||
|
var indexSlice []string
|
||||||
|
// For each file in listing
|
||||||
|
for _, file := range dirListing {
|
||||||
|
// If the file is not the key
|
||||||
|
if !strings.Contains(file.Name(), "savedKey.aesKey") {
|
||||||
|
// Append the file path to indexSlice
|
||||||
|
indexSlice = append(indexSlice, dir + "/" + file.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Join index slice into string
|
||||||
|
indexStr := strings.Join(indexSlice, ";")
|
||||||
|
// Write index to ResponseWriter
|
||||||
|
_, err = fmt.Fprint(res, indexStr)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||||
|
})
|
||||||
|
|
||||||
|
// If client connects to /key
|
||||||
|
http.HandleFunc("/key", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
// Inform user a client has requested the key
|
||||||
|
log.Info().Msg("GET Key")
|
||||||
|
// Read saved key
|
||||||
|
key, err := ioutil.ReadFile(dir + "/savedKey.aesKey")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading key") }
|
||||||
|
// Write saved key to ResponseWriter
|
||||||
|
_, err = fmt.Fprint(res, string(key))
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||||
|
})
|
||||||
|
|
||||||
|
// If client connects to /stop
|
||||||
|
http.HandleFunc("/stop", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
// Inform user a client has requested server shutdown
|
||||||
|
log.Info().Msg("GET Stop")
|
||||||
|
log.Info().Msg("Shutdown signal received")
|
||||||
|
// Shutdown server and send to empty context
|
||||||
|
err := srv.Shutdown(context.TODO())
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error stopping server") }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start HTTP Server
|
||||||
|
_ = srv.Serve(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get files from sender
|
||||||
|
func RecvFiles(dir string, senderAddr string) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Get server address by getting the IP without the port, prepending http:// and appending :9898
|
||||||
|
serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
|
||||||
|
// GET /index on sender's HTTP server
|
||||||
|
response, err := http.Get(serverAddr + "/index")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error getting index") }
|
||||||
|
// Close response body at the end of this function
|
||||||
|
defer response.Body.Close()
|
||||||
|
// Create index slice for storage of file index
|
||||||
|
var index []string
|
||||||
|
// If server responded with 200 OK
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
// Read response body
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading HTTP response") }
|
||||||
|
// Get string from body
|
||||||
|
bodyStr := string(body)
|
||||||
|
// Split string to form index
|
||||||
|
index = strings.Split(bodyStr, ";")
|
||||||
|
}
|
||||||
|
// For each file in the index
|
||||||
|
for _, file := range index {
|
||||||
|
// GET current file in index
|
||||||
|
response, err := http.Get(serverAddr + "/" + filepath.Base(file))
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error getting file") }
|
||||||
|
// If server responded with 200 OK
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
// Create new file at index filepath
|
||||||
|
newFile, err := os.Create(file)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||||
|
// Copy response body to new file
|
||||||
|
bytesWritten, err := io.Copy(newFile, response.Body)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
|
||||||
|
// Log bytes written
|
||||||
|
log.Info().Str("file", filepath.Base(file)).Msg("Wrote " + strconv.Itoa(int(bytesWritten)) + " bytes")
|
||||||
|
// Close new file
|
||||||
|
newFile.Close()
|
||||||
|
}
|
||||||
|
// Close response body
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send stop signal to sender's HTTP server
|
||||||
|
func SendSrvStopSignal(senderAddr string) {
|
||||||
|
// Get server address by getting the IP without the port, prepending http:// and appending :9898
|
||||||
|
serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
|
||||||
|
// GET /stop on sender's HTTP servers ignoring any errors
|
||||||
|
_, _ = http.Get(serverAddr + "/stop")
|
||||||
|
}
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module opensend
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/grandcat/zeroconf v1.0.0
|
||||||
|
github.com/pkg/browser v0.0.0-20201112035734-206646e67786
|
||||||
|
github.com/rs/zerolog v1.20.0
|
||||||
|
)
|
36
go.sum
Normal file
36
go.sum
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||||
|
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||||
|
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
||||||
|
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
|
github.com/pkg/browser v0.0.0-20201112035734-206646e67786 h1:4Gk0Dsp90g2YwfsxDOjvkEIgKGh+2R9FlvormRycveA=
|
||||||
|
github.com/pkg/browser v0.0.0-20201112035734-206646e67786/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||||
|
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
75
keyCrypto.go
Normal file
75
keyCrypto.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate RSA keypair
|
||||||
|
func GenerateRSAKeypair() (*rsa.PrivateKey, *rsa.PublicKey) {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Generate private/public RSA keypair
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error generating RSA keypair") }
|
||||||
|
// Get public key
|
||||||
|
publicKey := privateKey.PublicKey
|
||||||
|
// Return keypair
|
||||||
|
return privateKey, &publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get public key from sender
|
||||||
|
func GetKey(senderAddr string) []byte {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Get server address by getting the IP without the port, prepending http:// and appending :9898
|
||||||
|
serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
|
||||||
|
// GET /key on the sender's HTTP server
|
||||||
|
response, err := http.Get(serverAddr + "/key")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error getting key") }
|
||||||
|
// Close response body at the end of this function
|
||||||
|
defer response.Body.Close()
|
||||||
|
// If server responded with 200 OK
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
// Read response body into key
|
||||||
|
key, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error reading HTTP response") }
|
||||||
|
// Return key
|
||||||
|
return key
|
||||||
|
// Otherwise
|
||||||
|
} else {
|
||||||
|
// Fatally log status code
|
||||||
|
if err != nil { log.Fatal().Int("code", response.StatusCode).Msg("HTTP Error Response Code Received") }
|
||||||
|
}
|
||||||
|
// Return nil if all else fails
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt shared key with received public key
|
||||||
|
func EncryptKey(sharedKey string, recvPubKey *rsa.PublicKey) []byte {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Encrypt shared key using RSA
|
||||||
|
encryptedSharedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, recvPubKey, []byte(sharedKey), nil)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error encrypting shared key") }
|
||||||
|
// Return encrypted key
|
||||||
|
return encryptedSharedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt shared key using private RSA key
|
||||||
|
func DecryptKey(encryptedKey []byte, privateKey *rsa.PrivateKey) string {
|
||||||
|
// Decrypt shared key using RSA
|
||||||
|
decryptedKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error decrypting shared key") }
|
||||||
|
// Get string of decrypted key
|
||||||
|
sharedKey := string(decryptedKey)
|
||||||
|
// Return shared key
|
||||||
|
return sharedKey
|
||||||
|
}
|
65
keyExchange.go
Normal file
65
keyExchange.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/gob"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exchange keys with sender
|
||||||
|
func ReceiverKeyExchange(key *rsa.PublicKey) string {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Create TCP listener on port 9797
|
||||||
|
listener, err := net.Listen("tcp", ":9797")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
|
||||||
|
// Create string for sender address
|
||||||
|
var senderAddr string
|
||||||
|
// Create channel to send break signal
|
||||||
|
breakChannel := make(chan bool)
|
||||||
|
for {
|
||||||
|
// Accept connection on listener
|
||||||
|
connection, err := listener.Accept()
|
||||||
|
// Get sender address and store it in senderAddr
|
||||||
|
senderAddr = connection.RemoteAddr().String()
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error accepting connections") }
|
||||||
|
// Concurrently handle connection
|
||||||
|
go func(conn net.Conn) {
|
||||||
|
// Create gob encoder with connection as io.Writer
|
||||||
|
encoder := gob.NewEncoder(conn)
|
||||||
|
// Encode key into connection
|
||||||
|
err := encoder.Encode(key)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error encoding key") }
|
||||||
|
// Send signal to breakChannel
|
||||||
|
breakChannel <- true
|
||||||
|
}(connection)
|
||||||
|
// Wait for break signal
|
||||||
|
select {
|
||||||
|
// When break signal arrives
|
||||||
|
case _ = <-breakChannel:
|
||||||
|
// Return sender address
|
||||||
|
return senderAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchange keys with receiver
|
||||||
|
func SenderKeyExchange(receiverIP string) *rsa.PublicKey {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
// Connect to TCP socket on receiver IP port 9797
|
||||||
|
connection, err := net.Dial("tcp", receiverIP + ":9797")
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error connecting to sender") }
|
||||||
|
// Create gob decoder
|
||||||
|
decoder := gob.NewDecoder(connection)
|
||||||
|
// Instantiate rsa.PublicKey struct
|
||||||
|
recvPubKey := &rsa.PublicKey{}
|
||||||
|
// Decode key
|
||||||
|
err = decoder.Decode(recvPubKey)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error decoding key") }
|
||||||
|
// Return received key
|
||||||
|
return recvPubKey
|
||||||
|
}
|
137
main.go
Normal file
137
main.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Use ConsoleWriter logger
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
|
||||||
|
// Create 32 byte buffer
|
||||||
|
sharedKeyBytes := make([]byte, 32)
|
||||||
|
// Read random bytes into buffer
|
||||||
|
_, err := io.ReadFull(rand.Reader, sharedKeyBytes)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error generating random bytes") }
|
||||||
|
// Encode random bytes to hexadecimal
|
||||||
|
sharedKey := hex.EncodeToString(sharedKeyBytes)
|
||||||
|
// Get user's home directory
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error getting home directory") }
|
||||||
|
// Define opensend directory as ~/.opensend
|
||||||
|
opensendDir := homeDir + "/.opensend"
|
||||||
|
|
||||||
|
// Create channel for signals
|
||||||
|
sig := make(chan os.Signal, 1)
|
||||||
|
// Send message on channel upon reception of SIGINT or SIGTERM
|
||||||
|
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
||||||
|
// Intercept signal
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
// Wait for sig to be written
|
||||||
|
case <-sig:
|
||||||
|
// Warn user that a signal has been received and that opensend is shutting down
|
||||||
|
log.Warn().Msg("Signal received. Shutting down.")
|
||||||
|
// Remove opensend directory to avoid future conflicts
|
||||||
|
_ = os.RemoveAll(opensendDir)
|
||||||
|
// Exit with code 0
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create -t flag for type
|
||||||
|
actionType := flag.String("t", "","Type of data being sent")
|
||||||
|
// Create -d flag for data
|
||||||
|
actionData := flag.String("d", "", "Data to send")
|
||||||
|
// Create -s flag for sending
|
||||||
|
sendFlag := flag.Bool("s", false, "Send data")
|
||||||
|
// Create -r flag for receiving
|
||||||
|
recvFlag := flag.Bool("r", false, "Receive data")
|
||||||
|
// Parse flags
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Create opensend dir ignoring errors
|
||||||
|
_ = os.Mkdir(opensendDir, 0755)
|
||||||
|
// If -s given
|
||||||
|
if *sendFlag {
|
||||||
|
// Discover all _opensend._tcp.local. mDNS services
|
||||||
|
discoveredReceivers, discoveredIPs := DiscoverReceivers()
|
||||||
|
// Create reader for STDIN
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
// Print hostnames of each receiver
|
||||||
|
for index, receiver := range discoveredReceivers {
|
||||||
|
// Print hostname and index+1
|
||||||
|
fmt.Println("[" + strconv.Itoa(index + 1) + "]", receiver)
|
||||||
|
}
|
||||||
|
// Prompt user for choice
|
||||||
|
fmt.Print("Choose a receiver: ")
|
||||||
|
choiceStr, _ := reader.ReadString('\n')
|
||||||
|
// Convert input to int after trimming spaces
|
||||||
|
choiceInt, err := strconv.Atoi(strings.TrimSpace(choiceStr))
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error converting choice to int") }
|
||||||
|
// Set choiceIndex to choiceInt-1 to allow for indexing
|
||||||
|
choiceIndex := choiceInt - 1
|
||||||
|
// Get IP of chosen receiver
|
||||||
|
choiceIP := discoveredIPs[choiceIndex]
|
||||||
|
// Exchange RSA keys with receiver
|
||||||
|
rawKey := SenderKeyExchange(choiceIP)
|
||||||
|
// Encrypt shared key using RSA public key
|
||||||
|
key := EncryptKey(sharedKey, rawKey)
|
||||||
|
// Save encrypted key in opensend directory as savedKey.aesKey
|
||||||
|
SaveEncryptedKey(key, opensendDir + "/savedKey.aesKey")
|
||||||
|
// Instantiate Config object
|
||||||
|
config := NewConfig(*actionType, *actionData)
|
||||||
|
// Collect any files that may be required for transaction into opensend directory
|
||||||
|
config.CollectFiles(opensendDir)
|
||||||
|
// Create config file in opensend directory
|
||||||
|
config.CreateFile(opensendDir)
|
||||||
|
// Encrypt all files in opensend directory using shared key
|
||||||
|
EncryptFiles(opensendDir, sharedKey)
|
||||||
|
// Send all files in opensend directory using an HTTP server on port 9898
|
||||||
|
SendFiles(opensendDir)
|
||||||
|
// If -r given
|
||||||
|
} else if *recvFlag {
|
||||||
|
// Register {hostname}._opensend._tcp.local. mDNS service and pass shutdown function
|
||||||
|
zeroconfShutdown := RegisterService()
|
||||||
|
// Shutdown zeroconf server at the end of main()
|
||||||
|
defer zeroconfShutdown()
|
||||||
|
// Generate keypair
|
||||||
|
privateKey, publicKey := GenerateRSAKeypair()
|
||||||
|
// Exchange keys with sender
|
||||||
|
senderIP := ReceiverKeyExchange(publicKey)
|
||||||
|
// Sleep 300ms to allow sender time to start HTTP server
|
||||||
|
time.Sleep(300*time.Millisecond)
|
||||||
|
// Get files from sender and place them into the opensend directory
|
||||||
|
RecvFiles(opensendDir, senderIP)
|
||||||
|
// Get encrypted shared key from sender
|
||||||
|
encryptedKey := GetKey(senderIP)
|
||||||
|
// Send stop signal to sender's HTTP server
|
||||||
|
SendSrvStopSignal(senderIP)
|
||||||
|
// Decrypt shared key
|
||||||
|
sharedKey := DecryptKey(encryptedKey, privateKey)
|
||||||
|
// Decrypt all files in opensend directory using shared key
|
||||||
|
DecryptFiles(opensendDir, sharedKey)
|
||||||
|
// Instantiate Config
|
||||||
|
config := &Config{}
|
||||||
|
// Read config file in opensend directory
|
||||||
|
config.ReadFile(opensendDir + "/config.json")
|
||||||
|
// Execute JSON action using files within opensend directory
|
||||||
|
config.ExecuteAction(opensendDir)
|
||||||
|
}
|
||||||
|
// Remove opensend directory
|
||||||
|
err = os.RemoveAll(opensendDir)
|
||||||
|
if err != nil { log.Fatal().Err(err).Msg("Error remove opensend dir") }
|
||||||
|
}
|
Reference in New Issue
Block a user