Restructure project
This commit is contained in:
		
							
								
								
									
										80
									
								
								internal/transfer/deviceDiscovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								internal/transfer/deviceDiscovery.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								internal/transfer/transfer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								internal/transfer/transfer.go
									
									
									
									
									
										Normal 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")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user