Implement config files
This commit is contained in:
		
							
								
								
									
										290
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										290
									
								
								config.go
									
									
									
									
									
								
							@@ -1,278 +1,66 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"archive/tar"
 | 
						"errors"
 | 
				
			||||||
	"encoding/json"
 | 
						"github.com/pelletier/go-toml"
 | 
				
			||||||
	"github.com/pkg/browser"
 | 
					 | 
				
			||||||
	"github.com/rs/zerolog"
 | 
						"github.com/rs/zerolog"
 | 
				
			||||||
	"github.com/rs/zerolog/log"
 | 
						"github.com/rs/zerolog/log"
 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create config type to store action type and data
 | 
					 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	ActionType string
 | 
						Receiver ReceiverConfig
 | 
				
			||||||
	ActionData string
 | 
						Sender   SenderConfig
 | 
				
			||||||
 | 
						Targets  map[string]map[string]string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Instantiate and return a new Config struct
 | 
					type ReceiverConfig struct {
 | 
				
			||||||
func NewConfig(actionType string, actionData string) *Config {
 | 
						DestDir      string `toml:"destinationDirectory"`
 | 
				
			||||||
	return &Config{ActionType: actionType, ActionData: actionData}
 | 
						SkipZeroconf bool
 | 
				
			||||||
 | 
						WorkDir      string `toml:"workingDirectory"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (config *Config) Validate() {
 | 
					type SenderConfig struct {
 | 
				
			||||||
	if config.ActionType == "url" {
 | 
						WorkDir string `toml:"workingDirectory"`
 | 
				
			||||||
		// 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")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create config file
 | 
					func GetConfigPath() string {
 | 
				
			||||||
func (config *Config) CreateFile(dir string) {
 | 
					 | 
				
			||||||
	// Use ConsoleWriter logger
 | 
						// Use ConsoleWriter logger
 | 
				
			||||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
 | 
						log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
 | 
				
			||||||
	// Create config file at given directory
 | 
						configLocations := []string{"~/.config/opensend.toml", "/etc/opensend.toml"}
 | 
				
			||||||
	configFile, err := os.Create(dir + "/config.json")
 | 
						for _, configLocation := range configLocations {
 | 
				
			||||||
	if err != nil {
 | 
							expandedPath := ExpandPath(configLocation)
 | 
				
			||||||
		log.Fatal().Err(err).Msg("Error creating config file")
 | 
							if _, err := os.Stat(expandedPath); errors.Is(err, os.ErrNotExist) {
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// 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
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			// Set target path to header name in destination dir
 | 
							return expandedPath
 | 
				
			||||||
			targetPath := filepath.Join(dstDir, header.Name)
 | 
						}
 | 
				
			||||||
			switch header.Typeflag {
 | 
						return ""
 | 
				
			||||||
			// If regular file
 | 
					}
 | 
				
			||||||
			case tar.TypeReg:
 | 
					
 | 
				
			||||||
				// Try to create containing folder ignoring errors
 | 
					func NewConfig(path string) *Config {
 | 
				
			||||||
				_ = os.MkdirAll(filepath.Dir(targetPath), 0755)
 | 
						// Use ConsoleWriter logger
 | 
				
			||||||
				// Create file with mode contained in header at target path
 | 
						log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
 | 
				
			||||||
				dstFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
 | 
						newConfig := &Config{}
 | 
				
			||||||
 | 
						newConfig.SetDefaults()
 | 
				
			||||||
 | 
						if path != "" {
 | 
				
			||||||
 | 
							confData, err := ioutil.ReadFile(path)
 | 
				
			||||||
		if err != nil {
 | 
							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 = toml.Unmarshal(confData, newConfig)
 | 
				
			||||||
				_, err = io.Copy(dstFile, tarUnarchiver)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							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 {
 | 
					func (config *Config) SetDefaults() {
 | 
				
			||||||
		// Log unknown action type
 | 
						config.Receiver.DestDir = ExpandPath("~/Downloads")
 | 
				
			||||||
		log.Fatal().Msg("Unknown action type " + config.ActionType)
 | 
						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
 | 
								// Otherwise
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Create new file at index filepath
 | 
								// Create new file at index filepath
 | 
				
			||||||
			newFile, err := os.Create(opensendDir + "/" + file)
 | 
								newFile, err := os.Create(*workDir + "/" + file)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal().Err(err).Msg("Error creating file")
 | 
									log.Fatal().Err(err).Msg("Error creating file")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,6 +5,7 @@ go 1.15
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/grandcat/zeroconf v1.0.0
 | 
						github.com/grandcat/zeroconf v1.0.0
 | 
				
			||||||
	github.com/klauspost/compress v1.11.3
 | 
						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/pkg/browser v0.0.0-20201112035734-206646e67786
 | 
				
			||||||
	github.com/rs/zerolog v1.20.0
 | 
						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 h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 | 
				
			||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 | 
					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/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 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
 | 
				
			||||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
 | 
					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 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
 | 
				
			||||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
					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 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
 | 
				
			||||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 | 
					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 h1:4Gk0Dsp90g2YwfsxDOjvkEIgKGh+2R9FlvormRycveA=
 | 
				
			||||||
github.com/pkg/browser v0.0.0-20201112035734-206646e67786/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
 | 
					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.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 log event is fatal
 | 
				
			||||||
	if level == zerolog.FatalLevel {
 | 
						if level == zerolog.FatalLevel {
 | 
				
			||||||
		// Attempt removal of opensend directory
 | 
							// 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
 | 
							// Close connection
 | 
				
			||||||
		_ = hook.conn.Close()
 | 
							_ = hook.conn.Close()
 | 
				
			||||||
		// Attempt removal of opensend directory
 | 
							// Attempt removal of opensend directory
 | 
				
			||||||
		_ = os.RemoveAll(opensendDir)
 | 
							_ = os.RemoveAll(*workDir)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								main.go
									
									
									
									
									
								
							@@ -17,19 +17,43 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var opensendDir string
 | 
					var workDir *string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	// Use ConsoleWriter logger
 | 
						// Use ConsoleWriter logger
 | 
				
			||||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
 | 
						log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get user's home directory
 | 
						confPath := GetConfigPath()
 | 
				
			||||||
	homeDir, err := os.UserHomeDir()
 | 
						config := NewConfig(confPath)
 | 
				
			||||||
	if err != nil {
 | 
					
 | 
				
			||||||
		log.Fatal().Err(err).Msg("Error getting home directory")
 | 
						// 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
 | 
						// Create channel for signals
 | 
				
			||||||
	sig := make(chan os.Signal, 1)
 | 
						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
 | 
								// Warn user that a signal has been received and that opensend is shutting down
 | 
				
			||||||
			log.Warn().Msg("Signal received. Shutting down.")
 | 
								log.Warn().Msg("Signal received. Shutting down.")
 | 
				
			||||||
			// Remove opensend directory to avoid future conflicts
 | 
								// Remove opensend directory to avoid future conflicts
 | 
				
			||||||
			_ = os.RemoveAll(opensendDir)
 | 
								_ = os.RemoveAll(*workDir)
 | 
				
			||||||
			// Exit with code 0
 | 
								// Exit with code 0
 | 
				
			||||||
			os.Exit(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
 | 
						// Create opensend dir ignoring errors
 | 
				
			||||||
	_ = os.Mkdir(opensendDir, 0755)
 | 
						_ = os.Mkdir(*workDir, 0755)
 | 
				
			||||||
	// If -s given
 | 
						// If -s given
 | 
				
			||||||
	if *sendFlag {
 | 
						if *sendFlag {
 | 
				
			||||||
		if *actionType == "" || *actionData == "" {
 | 
							if *actionType == "" || *actionData == "" {
 | 
				
			||||||
@@ -119,13 +126,13 @@ func main() {
 | 
				
			|||||||
			choiceIP = discoveredIPs[choiceIndex]
 | 
								choiceIP = discoveredIPs[choiceIndex]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Instantiate Config object
 | 
							// Instantiate Config object
 | 
				
			||||||
		config := NewConfig(*actionType, *actionData)
 | 
							parameters := NewParameters(*actionType, *actionData)
 | 
				
			||||||
		// Validate data in config struct
 | 
							// Validate data in config struct
 | 
				
			||||||
		config.Validate()
 | 
							parameters.Validate()
 | 
				
			||||||
		// Collect any files that may be required for transaction into opensend directory
 | 
							// Collect any files that may be required for transaction into opensend directory
 | 
				
			||||||
		config.CollectFiles(opensendDir)
 | 
							parameters.CollectFiles(*workDir)
 | 
				
			||||||
		// Create config file in opensend directory
 | 
							// Create config file in opensend directory
 | 
				
			||||||
		config.CreateFile(opensendDir)
 | 
							parameters.CreateFile(*workDir)
 | 
				
			||||||
		// Notify user of key exchange
 | 
							// Notify user of key exchange
 | 
				
			||||||
		log.Info().Msg("Performing key exchange")
 | 
							log.Info().Msg("Performing key exchange")
 | 
				
			||||||
		// Exchange RSA keys with receiver
 | 
							// Exchange RSA keys with receiver
 | 
				
			||||||
@@ -135,15 +142,15 @@ func main() {
 | 
				
			|||||||
		// Encrypt shared key using RSA public key
 | 
							// Encrypt shared key using RSA public key
 | 
				
			||||||
		key := EncryptKey(sharedKey, rawKey)
 | 
							key := EncryptKey(sharedKey, rawKey)
 | 
				
			||||||
		// Save encrypted key in opensend directory as key.aes
 | 
							// 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
 | 
							// Notify user file encryption is beginning
 | 
				
			||||||
		log.Info().Msg("Encrypting files")
 | 
							log.Info().Msg("Encrypting files")
 | 
				
			||||||
		// Encrypt all files in opensend directory using shared key
 | 
							// Encrypt all files in opensend directory using shared key
 | 
				
			||||||
		EncryptFiles(opensendDir, sharedKey)
 | 
							EncryptFiles(*workDir, sharedKey)
 | 
				
			||||||
		// Notify user server has started
 | 
							// Notify user server has started
 | 
				
			||||||
		log.Info().Msg("Server started on port 9898")
 | 
							log.Info().Msg("Server started on port 9898")
 | 
				
			||||||
		// Send all files in opensend directory using an HTTP server on port 9898
 | 
							// Send all files in opensend directory using an HTTP server on port 9898
 | 
				
			||||||
		SendFiles(opensendDir)
 | 
							SendFiles(*workDir)
 | 
				
			||||||
		// If -r given
 | 
							// If -r given
 | 
				
			||||||
	} else if *recvFlag {
 | 
						} else if *recvFlag {
 | 
				
			||||||
		// If --skip-mdns is not given
 | 
							// If --skip-mdns is not given
 | 
				
			||||||
@@ -178,21 +185,21 @@ func main() {
 | 
				
			|||||||
		// Notify user file decryption is beginning
 | 
							// Notify user file decryption is beginning
 | 
				
			||||||
		log.Info().Msg("Decrypting files")
 | 
							log.Info().Msg("Decrypting files")
 | 
				
			||||||
		// Decrypt all files in opensend directory using shared key
 | 
							// Decrypt all files in opensend directory using shared key
 | 
				
			||||||
		DecryptFiles(opensendDir, sharedKey)
 | 
							DecryptFiles(*workDir, sharedKey)
 | 
				
			||||||
		// Instantiate Config
 | 
							// Instantiate Config
 | 
				
			||||||
		config := &Config{}
 | 
							parameters := &Parameters{}
 | 
				
			||||||
		// Read config file in opensend directory
 | 
							// Read config file in opensend directory
 | 
				
			||||||
		config.ReadFile(opensendDir + "/config.json")
 | 
							parameters.ReadFile(*workDir + "/parameters.json")
 | 
				
			||||||
		// Notify user that action is being executed
 | 
							// Notify user that action is being executed
 | 
				
			||||||
		log.Info().Msg("Executing JSON action")
 | 
							log.Info().Msg("Executing JSON action")
 | 
				
			||||||
		// Execute JSON action using files within opensend directory
 | 
							// Execute JSON action using files within opensend directory
 | 
				
			||||||
		config.ExecuteAction(opensendDir, *destDir)
 | 
							parameters.ExecuteAction(*workDir, *destDir)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		flag.Usage()
 | 
							flag.Usage()
 | 
				
			||||||
		log.Fatal().Msg("You must choose sender or receiver mode using -s or -r")
 | 
							log.Fatal().Msg("You must choose sender or receiver mode using -s or -r")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Remove opensend directory
 | 
						// Remove opensend directory
 | 
				
			||||||
	err = os.RemoveAll(opensendDir)
 | 
						err := os.RemoveAll(*workDir)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal().Err(err).Msg("Error removing opensend directory")
 | 
							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