forked from jriou/coller
- Remove encryptionKey from URL - Use POST method to pass both password and encryption key - Parse URL fragment to extract the encryption key from the web (using javascript) and from the CLI Fixes #36. Signed-off-by: Julien Riou <julien@riou.xyz>
178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
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())
|
|
}
|