From 6c634d8081a1bb3cc9ee5ebe37a4fefdce975f1d Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 4 Feb 2025 23:20:35 -0300 Subject: [PATCH] nak curl --- curl.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 11 ++++++ 2 files changed, 113 insertions(+) create mode 100644 curl.go diff --git a/curl.go b/curl.go new file mode 100644 index 0000000..a039f41 --- /dev/null +++ b/curl.go @@ -0,0 +1,102 @@ +package main + +import ( + "context" + "encoding/base64" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/fiatjaf/cli/v3" + "github.com/nbd-wtf/go-nostr" + "golang.org/x/exp/slices" +) + +var curlFlags []string + +var curl = &cli.Command{ + Name: "curl", + Usage: "calls curl but with a nip98 header", + Description: "accepts all flags and arguments exactly as they would be passed to curl.", + Flags: defaultKeyFlags, + Action: func(ctx context.Context, c *cli.Command) error { + kr, _, err := gatherKeyerFromArguments(ctx, c) + if err != nil { + return err + } + + // cowboy parsing of curl flags to get the data we need for nip98 + var url string + method := "GET" + + nextIsMethod := false + for _, f := range curlFlags { + if strings.HasPrefix(f, "https://") || strings.HasPrefix(f, "http://") { + url = f + } else if f == "--request" || f == "-X" { + nextIsMethod = true + } else if nextIsMethod { + method = f + method, _ = strings.CutPrefix(method, `"`) + method, _ = strings.CutSuffix(method, `"`) + method = strings.ToUpper(method) + } + nextIsMethod = false + } + + if url == "" { + return fmt.Errorf("can't create nip98 event: target url is empty") + } + + // make and sign event + evt := nostr.Event{ + Kind: 27235, + CreatedAt: nostr.Now(), + Tags: nostr.Tags{ + {"u", url}, + {"method", method}, + }, + } + if err := kr.SignEvent(ctx, &evt); err != nil { + return err + } + + // the first 2 indexes of curlFlags were reserved for this + curlFlags[0] = "-H" + curlFlags[1] = fmt.Sprintf("Authorization: Nostr %s", base64.StdEncoding.EncodeToString([]byte(evt.String()))) + + // call curl + cmd := exec.Command("curl", curlFlags...) + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Run() + return nil + }, +} + +func realCurl() error { + curlFlags = make([]string, 2, len(os.Args)-4) + keyFlags := make([]string, 0, 5) + + for i := 0; i < len(os.Args[2:]); i++ { + arg := os.Args[i+2] + if slices.ContainsFunc(defaultKeyFlags, func(f cli.Flag) bool { + bareArg, _ := strings.CutPrefix(arg, "-") + bareArg, _ = strings.CutPrefix(bareArg, "-") + return slices.Contains(f.Names(), bareArg) + }) { + keyFlags = append(keyFlags, arg) + if arg != "--prompt-sec" { + i++ + val := os.Args[i+2] + keyFlags = append(keyFlags, val) + } + } else { + curlFlags = append(curlFlags, arg) + } + } + + return curl.Run(context.Background(), keyFlags) +} diff --git a/main.go b/main.go index f2934fa..91aa68f 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ var app = &cli.Command{ outbox, wallet, mcpServer, + curl, }, Version: version, Flags: []cli.Flag{ @@ -140,6 +141,16 @@ func main() { Usage: "prints the version", } + // a megahack to enable this curl command proxy + if len(os.Args) > 2 && os.Args[1] == "curl" { + if err := realCurl(); err != nil { + stdout(err) + colors.reset() + os.Exit(1) + } + return + } + if err := app.Run(context.Background(), os.Args); err != nil { stdout(err) colors.reset()