Add a thin layer of comments on everything

main
diff 2021-03-25 19:52:32 +00:00
parent cc4913d573
commit 293d95b699
1 changed files with 37 additions and 4 deletions

41
main.go
View File

@ -38,15 +38,23 @@ import (
// Config stores all settings for an instance of RUFF. // Config stores all settings for an instance of RUFF.
type Config struct { type Config struct {
// Number of downloads to allow before exiting.
Downloads int Downloads int
// Port to use for the web server.
Port int Port int
// Path to the file being sent.
FilePath string FilePath string
// Name of the file being sent.
FileName string FileName string
// Hide the QR code of the final URL.
HideQR bool HideQR bool
// Start RUFF in upload mode, offering up an upload form instead of a file.
Uploading bool Uploading bool
// Allow uploads with multiple files selected.
Multiple bool Multiple bool
} }
// getConfig fills in a Config struct based on the command line arguments.
func getConfig() (Config, error) { func getConfig() (Config, error) {
conf := Config{ conf := Config{
Downloads: 1, Downloads: 1,
@ -79,6 +87,10 @@ func getConfig() (Config, error) {
return conf, nil return conf, nil
} }
// getIP uses the net package to try and determine the local address of the
// device it's running on.
//
// Note: no actual connections are made, just prepared.
func getIP() (string, error) { func getIP() (string, error) {
conn, err := net.Dial("udp", "8.8.8.8:80") conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil { if err != nil {
@ -93,6 +105,8 @@ func getIP() (string, error) {
return localAddr.IP.String(), nil return localAddr.IP.String(), nil
} }
// done is used to signal that the HTTP server has finished gracefully
// shutting down.
var done = make(chan struct{}) var done = make(chan struct{})
func main() { func main() {
@ -142,11 +156,18 @@ func main() {
} }
} }
// setupDownload sets up the HTTP server for sending a file to a remote device.
func setupDownload(server *http.Server, conf Config) { func setupDownload(server *http.Server, conf Config) {
downloads := conf.Downloads
http.Handle("/", http.RedirectHandler("/"+conf.FileName, http.StatusFound)) // 302 redirect http.Handle("/", http.RedirectHandler("/"+conf.FileName, http.StatusFound)) // 302 redirect
downloads := conf.Downloads
http.HandleFunc("/"+conf.FileName, func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/"+conf.FileName, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=\""+url.PathEscape(conf.FileName)+"\"") w.Header().Set("Content-Disposition", "attachment; filename=\""+url.PathEscape(conf.FileName)+"\"")
// http.ServeFile handles all the nitty gritty details of hauling the file
// off, but maybe it shouldn't? ServeFile does content ranges and I really
// don't see that working with limited download counts unless we reimplement
// all that logic ourselves.
http.ServeFile(w, r, conf.FilePath) http.ServeFile(w, r, conf.FilePath)
downloads-- downloads--
@ -198,6 +219,11 @@ var messageTemplate = `{{template "BaseHeader" (print "RUFF - " .)}}
<p>{{.}}</p> <p>{{.}}</p>
{{template "BaseFooter"}}` {{template "BaseFooter"}}`
// setupUpload sets up the HTTP server for receiving a file from another device
// through an upload form and a small stack of templates.
//
// When go1.16 gets more widespread maybe I'll hack the templates off into
// their own files.
func setupUpload(server *http.Server, conf Config) { func setupUpload(server *http.Server, conf Config) {
tpl := template.Must(template.New("BaseHeader").Parse(baseHeader)) tpl := template.Must(template.New("BaseHeader").Parse(baseHeader))
template.Must(tpl.New("BaseFooter").Parse(baseFooter)) template.Must(tpl.New("BaseFooter").Parse(baseFooter))
@ -206,7 +232,7 @@ func setupUpload(server *http.Server, conf Config) {
template.Must(tpl.New("UploadMessage").Parse(messageTemplate)) template.Must(tpl.New("UploadMessage").Parse(messageTemplate))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Upload form // Display upload form
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
err := tpl.ExecuteTemplate(w, "UploadForm", conf) err := tpl.ExecuteTemplate(w, "UploadForm", conf)
if err != nil { if err != nil {
@ -215,9 +241,12 @@ func setupUpload(server *http.Server, conf Config) {
return return
} }
// Handle upload // Handle POSTed upload
r.ParseMultipartForm(20 << 20) // Buffer a maximum of 20MB in memory. // Buffer a maximum of 20MB of form data in memory.
r.ParseMultipartForm(20 << 20)
// Collect all files from the form.
// They're stored in a map of slices of file headers.
files := make([]*multipart.FileHeader, 0, 1) files := make([]*multipart.FileHeader, 0, 1)
for _, field := range r.MultipartForm.File { for _, field := range r.MultipartForm.File {
for _, header := range field { for _, header := range field {
@ -244,6 +273,7 @@ func setupUpload(server *http.Server, conf Config) {
}) })
} }
// saveFile saves a fileHeader to the current working directory.
func saveFile(header *multipart.FileHeader) error { func saveFile(header *multipart.FileHeader) error {
inFile, err := header.Open() inFile, err := header.Open()
if err != nil { if err != nil {
@ -252,6 +282,8 @@ func saveFile(header *multipart.FileHeader) error {
defer inFile.Close() defer inFile.Close()
outFile, err := os.Create(header.Filename) outFile, err := os.Create(header.Filename)
// TODO: This might fail if the file already exists, we should handle this
// case specially.
if err != nil { if err != nil {
return err return err
} }
@ -265,6 +297,7 @@ func saveFile(header *multipart.FileHeader) error {
return nil return nil
} }
// shutdown shuts down the HTTP server, sending a signal when it's complete.
func shutdown(server *http.Server) { func shutdown(server *http.Server) {
server.Shutdown(context.Background()) server.Shutdown(context.Background())
done <- struct{}{} done <- struct{}{}