Implement config files
This commit is contained in:
parent
029b16a1a3
commit
9bd7b30222
290
config.go
290
config.go
@ -1,278 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"github.com/pkg/browser"
|
||||
"errors"
|
||||
"github.com/pelletier/go-toml"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Create config type to store action type and data
|
||||
type Config struct {
|
||||
ActionType string
|
||||
ActionData string
|
||||
Receiver ReceiverConfig
|
||||
Sender SenderConfig
|
||||
Targets map[string]map[string]string
|
||||
}
|
||||
|
||||
// Instantiate and return a new Config struct
|
||||
func NewConfig(actionType string, actionData string) *Config {
|
||||
return &Config{ActionType: actionType, ActionData: actionData}
|
||||
type ReceiverConfig struct {
|
||||
DestDir string `toml:"destinationDirectory"`
|
||||
SkipZeroconf bool
|
||||
WorkDir string `toml:"workingDirectory"`
|
||||
}
|
||||
|
||||
func (config *Config) Validate() {
|
||||
if config.ActionType == "url" {
|
||||
// Parse URL in config
|
||||
urlParser, err := url.Parse(config.ActionData)
|
||||
// If there was an error parsing
|
||||
if err != nil {
|
||||
// Alert user of invalid url
|
||||
log.Fatal().Err(err).Msg("Invalid URL")
|
||||
// If scheme is not detected
|
||||
} else if urlParser.Scheme == "" {
|
||||
// Alert user of invalid scheme
|
||||
log.Fatal().Msg("Invalid URL scheme")
|
||||
// If host is not detected
|
||||
} else if urlParser.Host == "" {
|
||||
// Alert user of invalid host
|
||||
log.Fatal().Msg("Invalid URL host")
|
||||
}
|
||||
}
|
||||
type SenderConfig struct {
|
||||
WorkDir string `toml:"workingDirectory"`
|
||||
}
|
||||
|
||||
// Create config file
|
||||
func (config *Config) CreateFile(dir string) {
|
||||
func GetConfigPath() string {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// 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}).Hook(FatalHook{})
|
||||
// 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)
|
||||
} else if config.ActionType == "dir" {
|
||||
// Create tar archive
|
||||
tarFile, err := os.Create(dir + "/" + filepath.Base(config.ActionData) + ".tar")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close tar file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create writer for tar archive
|
||||
tarArchiver := tar.NewWriter(tarFile)
|
||||
// Close archiver at the end of this function
|
||||
defer tarArchiver.Close()
|
||||
// Walk given directory
|
||||
err = filepath.Walk(config.ActionData, func(path string, info os.FileInfo, err error) error {
|
||||
// Return if error walking
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip if file is not normal mode
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
// Create tar header for file
|
||||
header, err := tar.FileInfoHeader(info, info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Change header name to reflect decompressed filepath
|
||||
header.Name = strings.TrimPrefix(strings.ReplaceAll(path, config.ActionData, ""), string(filepath.Separator))
|
||||
// Write header to archive
|
||||
if err := tarArchiver.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
// Open source file
|
||||
src, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Copy source bytes to tar archive
|
||||
if _, err := io.Copy(tarArchiver, src); err != nil {
|
||||
return err
|
||||
}
|
||||
// Return at the end of the function
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating tar archive")
|
||||
}
|
||||
// Set config data to base path for receiver
|
||||
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}).Hook(FatalHook{})
|
||||
// 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, destDir string) {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// 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()
|
||||
// Create file in user's Downloads directory
|
||||
dst, err := os.Create(filepath.Clean(destDir) + "/" + 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" {
|
||||
// Parse received URL
|
||||
urlParser, err := url.Parse(config.ActionData)
|
||||
// If there was an error parsing
|
||||
if err != nil {
|
||||
// Alert user of invalid url
|
||||
log.Fatal().Err(err).Msg("Invalid URL")
|
||||
// If scheme is not detected
|
||||
} else if urlParser.Scheme == "" {
|
||||
// Alert user of invalid scheme
|
||||
log.Fatal().Msg("Invalid URL scheme")
|
||||
// If host is not detected
|
||||
} else if urlParser.Host == "" {
|
||||
// Alert user of invalid host
|
||||
log.Fatal().Msg("Invalid URL host")
|
||||
}
|
||||
// Attempt to open URL in browser
|
||||
err = browser.OpenURL(config.ActionData)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening browser")
|
||||
}
|
||||
// If action is dir
|
||||
} else if config.ActionType == "dir" {
|
||||
// Set destination directory to ~/Downloads/{dir name}
|
||||
dstDir := filepath.Clean(destDir) + "/" + config.ActionData
|
||||
// Try to create destination directory
|
||||
err := os.MkdirAll(dstDir, 0755)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating directory")
|
||||
}
|
||||
// Try to open tar archive file
|
||||
tarFile, err := os.Open(srcDir + "/" + config.ActionData + ".tar")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening tar archive")
|
||||
}
|
||||
// Close tar archive file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create tar reader to unarchive tar archive
|
||||
tarUnarchiver := tar.NewReader(tarFile)
|
||||
// Loop to recursively unarchive tar file
|
||||
unarchiveLoop:
|
||||
for {
|
||||
// Jump to next header in tar archive
|
||||
header, err := tarUnarchiver.Next()
|
||||
switch {
|
||||
// If EOF
|
||||
case err == io.EOF:
|
||||
// break loop
|
||||
break unarchiveLoop
|
||||
case err != nil:
|
||||
log.Fatal().Err(err).Msg("Error unarchiving tar archive")
|
||||
// If nil header
|
||||
case header == nil:
|
||||
// Skip
|
||||
configLocations := []string{"~/.config/opensend.toml", "/etc/opensend.toml"}
|
||||
for _, configLocation := range configLocations {
|
||||
expandedPath := ExpandPath(configLocation)
|
||||
if _, err := os.Stat(expandedPath); errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
// Set target path to header name in destination dir
|
||||
targetPath := filepath.Join(dstDir, header.Name)
|
||||
switch header.Typeflag {
|
||||
// If regular file
|
||||
case tar.TypeReg:
|
||||
// Try to create containing folder ignoring errors
|
||||
_ = os.MkdirAll(filepath.Dir(targetPath), 0755)
|
||||
// Create file with mode contained in header at target path
|
||||
dstFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
return expandedPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func NewConfig(path string) *Config {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
newConfig := &Config{}
|
||||
newConfig.SetDefaults()
|
||||
if path != "" {
|
||||
confData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file during unarchiving")
|
||||
log.Fatal().Err(err).Msg("Error reading config")
|
||||
}
|
||||
// Copy data from tar archive into file
|
||||
_, err = io.Copy(dstFile, tarUnarchiver)
|
||||
err = toml.Unmarshal(confData, newConfig)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error copying data to file")
|
||||
log.Fatal().Err(err).Msg("Error unmarshalling toml")
|
||||
}
|
||||
}
|
||||
return newConfig
|
||||
}
|
||||
// Catchall
|
||||
} else {
|
||||
// Log unknown action type
|
||||
log.Fatal().Msg("Unknown action type " + config.ActionType)
|
||||
}
|
||||
|
||||
func (config *Config) SetDefaults() {
|
||||
config.Receiver.DestDir = ExpandPath("~/Downloads")
|
||||
config.Receiver.WorkDir = ExpandPath("~/.opensend")
|
||||
config.Receiver.SkipZeroconf = false
|
||||
config.Sender.WorkDir = ExpandPath("~/.opensend")
|
||||
config.Targets = map[string]map[string]string{}
|
||||
}
|
||||
|
24
extra.go
Normal file
24
extra.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExpandPath(s string) string {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting home directory")
|
||||
}
|
||||
expandedString := os.ExpandEnv(s)
|
||||
if strings.HasPrefix(expandedString, "~") {
|
||||
expandedString = strings.Replace(expandedString, "~", homeDir, 1)
|
||||
}
|
||||
expandedString = filepath.Clean(expandedString)
|
||||
return expandedString
|
||||
}
|
2
files.go
2
files.go
@ -223,7 +223,7 @@ func RecvFiles(connection net.Conn) {
|
||||
// Otherwise
|
||||
} else {
|
||||
// Create new file at index filepath
|
||||
newFile, err := os.Create(opensendDir + "/" + file)
|
||||
newFile, err := os.Create(*workDir + "/" + file)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.15
|
||||
require (
|
||||
github.com/grandcat/zeroconf v1.0.0
|
||||
github.com/klauspost/compress v1.11.3
|
||||
github.com/pelletier/go-toml v1.8.1
|
||||
github.com/pkg/browser v0.0.0-20201112035734-206646e67786
|
||||
github.com/rs/zerolog v1.20.0
|
||||
)
|
||||
|
4
go.sum
4
go.sum
@ -1,12 +1,16 @@
|
||||
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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
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/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
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=
|
||||
|
@ -15,7 +15,7 @@ func (hook FatalHook) Run(_ *zerolog.Event, level zerolog.Level, _ string) {
|
||||
// If log event is fatal
|
||||
if level == zerolog.FatalLevel {
|
||||
// Attempt removal of opensend directory
|
||||
_ = os.RemoveAll(opensendDir)
|
||||
_ = os.RemoveAll(*workDir)
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func (hook TCPFatalHook) Run(_ *zerolog.Event, level zerolog.Level, _ string) {
|
||||
// Close connection
|
||||
_ = hook.conn.Close()
|
||||
// Attempt removal of opensend directory
|
||||
_ = os.RemoveAll(opensendDir)
|
||||
_ = os.RemoveAll(*workDir)
|
||||
}
|
||||
|
||||
}
|
||||
|
83
main.go
83
main.go
@ -17,19 +17,43 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var opensendDir string
|
||||
var workDir *string
|
||||
|
||||
func main() {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
|
||||
// Get user's home directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting home directory")
|
||||
confPath := GetConfigPath()
|
||||
config := NewConfig(confPath)
|
||||
|
||||
// Create --send-to flag to send to a specific IP
|
||||
sendTo := flag.String("send-to", "", "Use IP address of receiver instead of mDNS")
|
||||
// Create --dest-dir flag to save to a specified folder
|
||||
destDir := flag.String("dest-dir", "", "Destination directory for files or dirs sent over opensend")
|
||||
workDir = flag.String("work-dir", "", "Working directory for opensend")
|
||||
givenCfgPath := flag.String("config", "", "Opensend config to use")
|
||||
// Create --skip-mdns to skip service registration
|
||||
skipMdns := flag.Bool("skip-mdns", config.Receiver.SkipZeroconf, "Skip zeroconf service registration (use if mdns fails)")
|
||||
// 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()
|
||||
if *givenCfgPath != "" {
|
||||
config = NewConfig(*givenCfgPath)
|
||||
}
|
||||
if *workDir == "" {
|
||||
if *sendFlag {
|
||||
*workDir = ExpandPath(config.Sender.WorkDir)
|
||||
} else {
|
||||
*workDir = ExpandPath(config.Receiver.WorkDir)
|
||||
}
|
||||
}
|
||||
// Define opensend directory as ~/.opensend
|
||||
opensendDir = homeDir + "/.opensend"
|
||||
|
||||
// Create channel for signals
|
||||
sig := make(chan os.Signal, 1)
|
||||
@ -43,31 +67,14 @@ func main() {
|
||||
// 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)
|
||||
_ = os.RemoveAll(*workDir)
|
||||
// Exit with code 0
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
// Create --send-to flag to send to a specific IP
|
||||
sendTo := flag.String("send-to", "", "Use IP address of receiver instead of mDNS")
|
||||
// Create --dest-dir flag to save to a specified folder
|
||||
destDir := flag.String("dest-dir", homeDir+"/Downloads", "Destination directory for files or dirs sent over opensend")
|
||||
// Create --skip-mdns to skip service registration
|
||||
skipMdns := flag.Bool("skip-mdns", false, "Skip zeroconf service registration (use if mdns fails)")
|
||||
// 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)
|
||||
_ = os.Mkdir(*workDir, 0755)
|
||||
// If -s given
|
||||
if *sendFlag {
|
||||
if *actionType == "" || *actionData == "" {
|
||||
@ -119,13 +126,13 @@ func main() {
|
||||
choiceIP = discoveredIPs[choiceIndex]
|
||||
}
|
||||
// Instantiate Config object
|
||||
config := NewConfig(*actionType, *actionData)
|
||||
parameters := NewParameters(*actionType, *actionData)
|
||||
// Validate data in config struct
|
||||
config.Validate()
|
||||
parameters.Validate()
|
||||
// Collect any files that may be required for transaction into opensend directory
|
||||
config.CollectFiles(opensendDir)
|
||||
parameters.CollectFiles(*workDir)
|
||||
// Create config file in opensend directory
|
||||
config.CreateFile(opensendDir)
|
||||
parameters.CreateFile(*workDir)
|
||||
// Notify user of key exchange
|
||||
log.Info().Msg("Performing key exchange")
|
||||
// Exchange RSA keys with receiver
|
||||
@ -135,15 +142,15 @@ func main() {
|
||||
// Encrypt shared key using RSA public key
|
||||
key := EncryptKey(sharedKey, rawKey)
|
||||
// Save encrypted key in opensend directory as key.aes
|
||||
SaveEncryptedKey(key, opensendDir+"/key.aes")
|
||||
SaveEncryptedKey(key, *workDir+"/key.aes")
|
||||
// Notify user file encryption is beginning
|
||||
log.Info().Msg("Encrypting files")
|
||||
// Encrypt all files in opensend directory using shared key
|
||||
EncryptFiles(opensendDir, sharedKey)
|
||||
EncryptFiles(*workDir, sharedKey)
|
||||
// Notify user server has started
|
||||
log.Info().Msg("Server started on port 9898")
|
||||
// Send all files in opensend directory using an HTTP server on port 9898
|
||||
SendFiles(opensendDir)
|
||||
SendFiles(*workDir)
|
||||
// If -r given
|
||||
} else if *recvFlag {
|
||||
// If --skip-mdns is not given
|
||||
@ -178,21 +185,21 @@ func main() {
|
||||
// Notify user file decryption is beginning
|
||||
log.Info().Msg("Decrypting files")
|
||||
// Decrypt all files in opensend directory using shared key
|
||||
DecryptFiles(opensendDir, sharedKey)
|
||||
DecryptFiles(*workDir, sharedKey)
|
||||
// Instantiate Config
|
||||
config := &Config{}
|
||||
parameters := &Parameters{}
|
||||
// Read config file in opensend directory
|
||||
config.ReadFile(opensendDir + "/config.json")
|
||||
parameters.ReadFile(*workDir + "/parameters.json")
|
||||
// Notify user that action is being executed
|
||||
log.Info().Msg("Executing JSON action")
|
||||
// Execute JSON action using files within opensend directory
|
||||
config.ExecuteAction(opensendDir, *destDir)
|
||||
parameters.ExecuteAction(*workDir, *destDir)
|
||||
} else {
|
||||
flag.Usage()
|
||||
log.Fatal().Msg("You must choose sender or receiver mode using -s or -r")
|
||||
}
|
||||
// Remove opensend directory
|
||||
err = os.RemoveAll(opensendDir)
|
||||
err := os.RemoveAll(*workDir)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error removing opensend directory")
|
||||
}
|
||||
|
278
parameterSerialization.go
Normal file
278
parameterSerialization.go
Normal file
@ -0,0 +1,278 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Create config type to store action type and data
|
||||
type Parameters struct {
|
||||
ActionType string
|
||||
ActionData string
|
||||
}
|
||||
|
||||
// Instantiate and return a new Config struct
|
||||
func NewParameters(actionType string, actionData string) *Parameters {
|
||||
return &Parameters{ActionType: actionType, ActionData: actionData}
|
||||
}
|
||||
|
||||
func (parameters *Parameters) Validate() {
|
||||
if parameters.ActionType == "url" {
|
||||
// Parse URL in parameters
|
||||
urlParser, err := url.Parse(parameters.ActionData)
|
||||
// If there was an error parsing
|
||||
if err != nil {
|
||||
// Alert user of invalid url
|
||||
log.Fatal().Err(err).Msg("Invalid URL")
|
||||
// If scheme is not detected
|
||||
} else if urlParser.Scheme == "" {
|
||||
// Alert user of invalid scheme
|
||||
log.Fatal().Msg("Invalid URL scheme")
|
||||
// If host is not detected
|
||||
} else if urlParser.Host == "" {
|
||||
// Alert user of invalid host
|
||||
log.Fatal().Msg("Invalid URL host")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create config file
|
||||
func (parameters *Parameters) CreateFile(dir string) {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create parameters file at given directory
|
||||
configFile, err := os.Create(dir + "/parameters.json")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating parameters file")
|
||||
}
|
||||
// Close parameters file at the end of this function
|
||||
defer configFile.Close()
|
||||
// Marshal given Parameters struct into a []byte
|
||||
jsonData, err := json.Marshal(parameters)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error encoding JSON")
|
||||
}
|
||||
// Write []byte to previously created parameters 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", "parameters.json").Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||
}
|
||||
|
||||
// Collect all required files into given directory
|
||||
func (parameters *Parameters) CollectFiles(dir string) {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// If action type is file
|
||||
if parameters.ActionType == "file" {
|
||||
// Open file path in parameters.ActionData
|
||||
src, err := os.Open(parameters.ActionData)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening file from parameters")
|
||||
}
|
||||
// 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(parameters.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 parameters.ActionData with file name
|
||||
parameters.ActionData = filepath.Base(parameters.ActionData)
|
||||
} else if parameters.ActionType == "dir" {
|
||||
// Create tar archive
|
||||
tarFile, err := os.Create(dir + "/" + filepath.Base(parameters.ActionData) + ".tar")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close tar file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create writer for tar archive
|
||||
tarArchiver := tar.NewWriter(tarFile)
|
||||
// Close archiver at the end of this function
|
||||
defer tarArchiver.Close()
|
||||
// Walk given directory
|
||||
err = filepath.Walk(parameters.ActionData, func(path string, info os.FileInfo, err error) error {
|
||||
// Return if error walking
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip if file is not normal mode
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
// Create tar header for file
|
||||
header, err := tar.FileInfoHeader(info, info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Change header name to reflect decompressed filepath
|
||||
header.Name = strings.TrimPrefix(strings.ReplaceAll(path, parameters.ActionData, ""), string(filepath.Separator))
|
||||
// Write header to archive
|
||||
if err := tarArchiver.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
// Open source file
|
||||
src, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Copy source bytes to tar archive
|
||||
if _, err := io.Copy(tarArchiver, src); err != nil {
|
||||
return err
|
||||
}
|
||||
// Return at the end of the function
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating tar archive")
|
||||
}
|
||||
// Set parameters data to base path for receiver
|
||||
parameters.ActionData = filepath.Base(parameters.ActionData)
|
||||
}
|
||||
}
|
||||
|
||||
// Read config file at given file path
|
||||
func (parameters *Parameters) ReadFile(filePath string) {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Read file at filePath
|
||||
fileData, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading parameters file")
|
||||
}
|
||||
// Unmarshal data from JSON into parameters struct
|
||||
err = json.Unmarshal(fileData, parameters)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decoding JSON")
|
||||
}
|
||||
}
|
||||
|
||||
// Execute action specified in config
|
||||
func (parameters *Parameters) ExecuteAction(srcDir string, destDir string) {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// If action is file
|
||||
if parameters.ActionType == "file" {
|
||||
// Open file from parameters at given directory
|
||||
src, err := os.Open(srcDir + "/" + parameters.ActionData)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading file from parameters")
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Create file in user's Downloads directory
|
||||
dst, err := os.Create(filepath.Clean(destDir) + "/" + parameters.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 parameters.ActionType == "url" {
|
||||
// Parse received URL
|
||||
urlParser, err := url.Parse(parameters.ActionData)
|
||||
// If there was an error parsing
|
||||
if err != nil {
|
||||
// Alert user of invalid url
|
||||
log.Fatal().Err(err).Msg("Invalid URL")
|
||||
// If scheme is not detected
|
||||
} else if urlParser.Scheme == "" {
|
||||
// Alert user of invalid scheme
|
||||
log.Fatal().Msg("Invalid URL scheme")
|
||||
// If host is not detected
|
||||
} else if urlParser.Host == "" {
|
||||
// Alert user of invalid host
|
||||
log.Fatal().Msg("Invalid URL host")
|
||||
}
|
||||
// Attempt to open URL in browser
|
||||
err = browser.OpenURL(parameters.ActionData)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening browser")
|
||||
}
|
||||
// If action is dir
|
||||
} else if parameters.ActionType == "dir" {
|
||||
// Set destination directory to ~/Downloads/{dir name}
|
||||
dstDir := filepath.Clean(destDir) + "/" + parameters.ActionData
|
||||
// Try to create destination directory
|
||||
err := os.MkdirAll(dstDir, 0755)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating directory")
|
||||
}
|
||||
// Try to open tar archive file
|
||||
tarFile, err := os.Open(srcDir + "/" + parameters.ActionData + ".tar")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening tar archive")
|
||||
}
|
||||
// Close tar archive file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create tar reader to unarchive tar archive
|
||||
tarUnarchiver := tar.NewReader(tarFile)
|
||||
// Loop to recursively unarchive tar file
|
||||
unarchiveLoop:
|
||||
for {
|
||||
// Jump to next header in tar archive
|
||||
header, err := tarUnarchiver.Next()
|
||||
switch {
|
||||
// If EOF
|
||||
case err == io.EOF:
|
||||
// break loop
|
||||
break unarchiveLoop
|
||||
case err != nil:
|
||||
log.Fatal().Err(err).Msg("Error unarchiving tar archive")
|
||||
// If nil header
|
||||
case header == nil:
|
||||
// Skip
|
||||
continue
|
||||
}
|
||||
// Set target path to header name in destination dir
|
||||
targetPath := filepath.Join(dstDir, header.Name)
|
||||
switch header.Typeflag {
|
||||
// If regular file
|
||||
case tar.TypeReg:
|
||||
// Try to create containing folder ignoring errors
|
||||
_ = os.MkdirAll(filepath.Dir(targetPath), 0755)
|
||||
// Create file with mode contained in header at target path
|
||||
dstFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file during unarchiving")
|
||||
}
|
||||
// Copy data from tar archive into file
|
||||
_, err = io.Copy(dstFile, tarUnarchiver)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error copying data to file")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Catchall
|
||||
} else {
|
||||
// Log unknown action type
|
||||
log.Fatal().Msg("Unknown action type " + parameters.ActionType)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user