package main import "os" import "fmt" import "time" import "mime" import "bytes" import "strings" import "net/http" import "io/ioutil" import "path/filepath" import "mime/multipart" import "net/textproto" import "github.com/alecthomas/kong" var Args struct { Upload struct { Filename string `optional:"" short:"n" help:"A short name by which the file can be accessed"` File string `arg:"" help:"The URL or a path to a file to upload"` Instance string `arg:"" help:"The URL of the instance to upload the file to"` Tos bool `short:"t" default:"false"` } `cmd:"" help:"Upload a file"` } func upload(args *kong.Context) { if strings.HasSuffix(Args.Upload.Instance, "/") { fmt.Fprintf(os.Stderr, "Error: instance URL must not end with /\n") os.Exit(1) } if ! Args.Upload.Tos { fmt.Println("You must agree to the ToS!"); fmt.Println("It can be found at", Args.Upload.Instance + "/tos") os.Exit(2) } if _, err := os.Stat(Args.Upload.File); err != nil { fmt.Fprintf(os.Stderr, "Error: can't read file %s\n", Args.Upload.File) os.Exit(1) } body := &bytes.Buffer{} writer := multipart.NewWriter(body) ftype := mime.TypeByExtension(filepath.Ext(Args.Upload.File)) if ftype == "" { ftype = "application/x-octet-stream" } fileHeader := textproto.MIMEHeader{} fileHeader.Set("Content-Type", ftype) fileHeader.Set("Content-Disposition", fmt.Sprintf("form-data; name=\"file\"; filename=\"%s\"", Args.Upload.File)) part, err := writer.CreatePart(fileHeader) if err != nil { panic(err) } tosHeader := textproto.MIMEHeader{} tosHeader.Set("Content-Type", "text/plain") tosHeader.Set("Content-Disposition", fmt.Sprintf("form-data; name=\"tos_consent\"; filename=\"%s\"", Args.Upload.File)) tos, err := writer.CreatePart(tosHeader) if err != nil { panic(err) } tos.Write([]byte("on")) file, err := ioutil.ReadFile(Args.Upload.File) if err != nil { panic(err) } part.Write([]byte(file)) writer.Close() req, err := http.NewRequest("POST", Args.Upload.Instance + "/curlapi/upload", body) if err != nil { panic(err) } cType := writer.FormDataContentType() req.Header.Set("Content-Type", cType) req.Header.Set("Content-Length", fmt.Sprintf("%d", body.Len())) client := &http.Client{Timeout: 120 * time.Second} api, err := client.Do(req) if err != nil { panic(err) } defer api.Body.Close() data, err := ioutil.ReadAll(api.Body) if err != nil { panic(err) } fmt.Println(string(data)) } func main() { args := kong.Parse(&Args) switch args.Command() { case "upload ": upload(args) default: panic(args.Command()) } }