package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log/slog" "net/http" "net/url" "os" "syscall" "golang.org/x/term" "git.riou.xyz/jriou/coller/internal" ) var ( AppName = "copier" AppVersion string GoVersion string GitCommit string ) type NotePayload struct { Password string `json:"password"` } func handleMain() int { flag.Usage = usage version := flag.Bool("version", false, "Print version and exit") quiet := flag.Bool("quiet", false, "Log errors only") verbose := flag.Bool("verbose", false, "Print more logs") debug := flag.Bool("debug", false, "Print even more logs") encryptionKey := flag.String("encryption-key", os.Getenv("COLLER_ENCRYPTION_KEY"), "Key to decrypt the note") askEncryptionKey := flag.Bool("ask-encryption-key", false, "Read encryption key from input") fileName := flag.String("file", "", "Write content of the note to a file") bearer := flag.String("bearer", os.Getenv("COLLER_BEARER"), "Bearer token") askBearer := flag.Bool("ask-bearer", false, "Read bearer token from input") password := flag.String("password", os.Getenv("COLLER_PASSWORD"), "Password to access the note") flag.Parse() if *version { internal.ShowVersion(AppName, AppVersion, GitCommit, GoVersion) return internal.RC_OK } if flag.NArg() != 1 { usage() return internal.RC_ERROR } rawURL := flag.Args()[0] var level slog.Level if *debug { level = slog.LevelDebug } if *verbose { level = slog.LevelInfo } if *quiet { level = slog.LevelError } logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level})) if *askEncryptionKey { fmt.Print("Encryption key: ") p, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { return internal.ReturnError(logger, "could not read encryption key", err) } *encryptionKey = string(p) fmt.Print("\n") } if *askBearer { fmt.Print("Bearer: ") b, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { return internal.ReturnError(logger, "could not read bearer token", err) } *bearer = string(b) fmt.Print("\n") } logger.Debug("parsing url", slog.Any("url", rawURL)) u, err := url.Parse(rawURL) if err != nil { return internal.ReturnError(logger, "could not parse url", err) } u.Path = "api/note" + u.Path if u.Fragment != "" { *encryptionKey = u.Fragment u.Fragment = "" } rawURL = u.String() logger.Debug("creating http request") var req *http.Request if *password != "" { body := &NotePayload{ Password: *password, } payload, err := json.Marshal(body) if err != nil { return internal.ReturnError(logger, "could not create note payload", err) } req, err = http.NewRequest("POST", rawURL, bytes.NewBuffer(payload)) if err != nil { return internal.ReturnError(logger, "could not create request", err) } } else { req, err = http.NewRequest("GET", rawURL, nil) if err != nil { return internal.ReturnError(logger, "could not create request", err) } } if *bearer != "" { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *bearer)) } logger.Debug("executing http request", slog.Any("method", req.Method), slog.Any("url", rawURL)) r, err := http.DefaultClient.Do(req) if err != nil { return internal.ReturnError(logger, "could not retreive note", err) } if r.StatusCode >= 300 { return internal.ReturnError(logger, "could not retreive note", fmt.Errorf("status code %d", r.StatusCode)) } logger.Debug("decoding body") body, err := io.ReadAll(r.Body) if err != nil { return internal.ReturnError(logger, "could not read response", err) } var content []byte if *encryptionKey != "" { logger.Debug("decrypting note") content, err = internal.Decrypt(body, *encryptionKey) if err != nil { return internal.ReturnError(logger, "could not decrypt note", err) } } else { content = body } if *fileName != "" { logger.Debug("writing output to file", slog.Any("file", *fileName)) err = os.WriteFile(*fileName, content, 0644) if err != nil { return internal.ReturnError(logger, "could not write output to file", err) } } else { fmt.Printf("%s", content) } return internal.RC_OK } func usage() { fmt.Printf("Usage: %s [OPTIONS] URL\n", os.Args[0]) flag.PrintDefaults() } func main() { os.Exit(handleMain()) }