// The hole command is a CLI uploader tool for the roseh.moe wormhole. // // # Magic wormhole // // The name is inspired by the original "magic wormhole" library. The goal is to // get a file from one computer to another without a USB stick, Including cases // where scp might not be available. This includes transferring files to your // phone, for example, although a USB cord might still be more convenient when // moving many files. It should also work over restrictive firewalls. You should // be able to use the wormhole as long as you can access http://roseh.moe in // a browser. // // # Usage example // // The following shows a typical interaction with the tool // // $ hole file.pdf // https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any // Waiting for upload... // // // Then, on another machine: // // $ curl https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any > file.pdf // // # Web interface // // There's also a web interface available for the wormhole, available at // https://roseh.moe/wormhole. package main import ( "flag" "fmt" "io" "mime/multipart" "net/http" "os" "gitlab.com/rhogenson/roseh.moe/internal/hole" ) var url = flag.String("url", "https://roseh.moe", "server URL") func upload() error { args := flag.Args() if len(args) == 0 { return fmt.Errorf("usage error: missing filename") } if len(args) > 1 { return fmt.Errorf("usage error: too many positional arguments") } fileName := args[0] f, err := os.Open(fileName) if err != nil { return err } defer f.Close() hole := hole.New() fmt.Println(*url + "/wormhole/" + hole) fmt.Fprintln(os.Stderr, "Waiting for upload...") resp, err := http.Get(*url + "/wormhole/" + hole + "/ready") if err != nil { return fmt.Errorf("check wormhole ready: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("check wormhole ready: status: %s", resp.Status) } if _, err := io.Copy(io.Discard, resp.Body); err != nil { return fmt.Errorf("check wormhole ready: read response: %s", err) } pipeReader, pipeWriter := io.Pipe() multipartWriter := multipart.NewWriter(pipeWriter) go func() { defer pipeWriter.Close() part, err := multipartWriter.CreateFormFile("file", fileName) if err != nil { pipeWriter.CloseWithError(fmt.Errorf("create multipart reader: %s", err)) return } if _, err := io.Copy(part, f); err != nil { pipeWriter.CloseWithError(fmt.Errorf("write body: %s", err)) return } if err := multipartWriter.Close(); err != nil { pipeWriter.CloseWithError(fmt.Errorf("write trailer: %s", err)) return } }() resp, err = http.Post(*url+"/wormhole/"+hole, multipartWriter.FormDataContentType(), pipeReader) if err != nil { return fmt.Errorf("upload: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("upload: status: %s", resp.Status) } io.Copy(os.Stderr, resp.Body) fmt.Fprintln(os.Stderr) return nil } func main() { flag.Usage = func() { fmt.Fprint(os.Stderr, `Usage: hole FILE Create a one-time wormhole that allows a specific client to download a file. Prints the URL to download the file and then hangs until the file is downloaded. Example: $ hole file.pdf https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any Waiting for upload... Then, on another machine: $ curl https://roseh.moe/wormhole/dv-32-ethos-aztec-shako-spoils-2y-dwarf-sand-any > file.pdf Flags: `) flag.PrintDefaults() } flag.Parse() if err := upload(); err != nil { fmt.Fprintf(os.Stderr, "hole: %s\n", err) os.Exit(1) } }