Restructure project

This commit is contained in:
2021-07-08 13:11:41 -07:00
parent 8099077c50
commit 74c6391dca
12 changed files with 43 additions and 369 deletions

View File

@@ -0,0 +1,80 @@
/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package transfer
import (
"context"
"os"
"time"
"github.com/grandcat/zeroconf"
"github.com/rs/zerolog/log"
)
// Discover opensend receivers on the network
func DiscoverReceivers() ([]string, []string) {
// Use ConsoleWriter logger
// 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
}

View File

@@ -0,0 +1,189 @@
/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package transfer
import (
"io"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/rs/zerolog/log"
)
// Save encrypted key to file
func SaveEncryptedKey(encryptedKey []byte, filePath string) {
// Use ConsoleWriter logger
// 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 with normal FatalHook
// Create TCP listener on port 9898
listener, err := net.Listen("tcp", ":9898")
if err != nil {
log.Fatal().Err(err).Msg("Error starting listener")
}
http.HandleFunc("/key", func(res http.ResponseWriter, req *http.Request) {
// Inform user client has requested key
log.Info().Msg("Key requested")
// Read saved key
key, err := ioutil.ReadFile(dir + "/key.aes")
if err != nil {
log.Fatal().Err(err).Msg("Error reading key")
}
// Write saved key to ResponseWriter
_, err = res.Write(key)
if err != nil {
log.Fatal().Err(err).Msg("Error writing response")
}
})
http.HandleFunc("/index", func(res http.ResponseWriter, req *http.Request) {
// Inform user a client has requested the file index
log.Info().Msg("Index requested")
// 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(), "key.aes") {
// Append the file path to indexSlice
indexSlice = append(indexSlice, file.Name())
}
}
// Join index slice into string
indexStr := strings.Join(indexSlice, "|")
// Write index to ResponseWriter
_, err = res.Write([]byte(indexStr))
if err != nil {
log.Fatal().Err(err).Msg("Error writing response")
}
})
http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
log.Info().Str("file", filepath.Base(req.URL.Path)).Msg("File requested")
http.FileServer(http.Dir(dir)).ServeHTTP(res, req)
})
http.HandleFunc("/stop", func(res http.ResponseWriter, req *http.Request) {
log.Info().Msg("Stop signal received")
res.WriteHeader(http.StatusOK)
listener.Close()
})
http.Serve(listener, nil)
}
type Sender struct {
RemoteAddr string
}
func (c *Sender) Get(endpoint string) (io.ReadCloser, int, error) {
res, err := http.Get(c.RemoteAddr + endpoint)
if err != nil {
return nil, 0, err
}
return res.Body, res.StatusCode, nil
}
func NewSender(senderAddr string) *Sender {
// Get server address by getting the IP without the port, and appending :9898
host, _, _ := net.SplitHostPort(senderAddr)
serverAddr := "http://" + net.JoinHostPort(host, "9898")
return &Sender{RemoteAddr: serverAddr}
}
// Get files from sender
func RecvFiles(sender *Sender, workDir string) {
// Use ConsoleWriter logger
indexReader, code, err := sender.Get("/index")
if err != nil {
log.Fatal().Err(err).Msg("Error getting index")
}
// If non-ok code returned, fatally log
if code != http.StatusOK {
log.Fatal().Err(err).Msg("Sender reported error")
}
indexBytes, err := ioutil.ReadAll(indexReader)
if err != nil {
log.Fatal().Err(err).Msg("Error reading index from response")
}
// Get index from message
index := strings.Split(strings.TrimSpace(string(indexBytes)), "|")
for _, file := range index {
// Read received message
fileData, code, err := sender.Get("/" + file)
if err != nil {
log.Fatal().Err(err).Msg("Error getting file")
}
// If non-ok code returned
if code != http.StatusOK {
// fatally log
log.Fatal().
Int("status", code).
Str("statusText", http.StatusText(code)).
Err(err).
Msg("Sender reported error")
// Otherwise
} else {
// Create new file at index filepath
newFile, err := os.Create(workDir + "/" + file)
if err != nil {
log.Fatal().Err(err).Msg("Error creating file")
}
// Copy response body to new file
bytesWritten, err := io.Copy(newFile, fileData)
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()
}
}
}
// Send stop signal to sender
func SendSrvStopSignal(sender *Sender) {
_, _, _ = sender.Get("/stop")
}