summaryrefslogtreecommitdiff
path: root/tools/sw-fetch/main.go
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-09-03 08:01:38 -0600
committertjpcc <tjp@ctrl-c.club>2023-09-03 08:01:38 -0600
commit5befdc9c851f285000c15abc01a08010c719b307 (patch)
tree105808811158cd3df80f0cc21f53b77c73c4e4fa /tools/sw-fetch/main.go
parenta918c9d02d8558e612be84daac3c28204bb0f93f (diff)
sw-convert and sw-fetch tools
Diffstat (limited to 'tools/sw-fetch/main.go')
-rw-r--r--tools/sw-fetch/main.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/tools/sw-fetch/main.go b/tools/sw-fetch/main.go
new file mode 100644
index 0000000..ccf8ac8
--- /dev/null
+++ b/tools/sw-fetch/main.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net/url"
+ "os"
+
+ "tildegit.org/tjp/sliderule"
+ "tildegit.org/tjp/sliderule/gemini"
+)
+
+const usage = `Resource fetcher for the small web.
+
+Usage:
+ sw-fetch (-h | --help)
+ sw-fetch [-v | --verbose] [-o PATH | --output PATH] [-k | --keyfile PATH] [ -c | --certfile PATH ] [ -s | --skip-verify ] URL
+
+Options:
+ -h --help Show this screen.
+ -v --verbose Display more diagnostic information on standard error.
+ -o --output PATH Send the fetched resource to PATH instead of standard out.
+ -k --keyfile PATH Path to the TLS key file to use.
+ -c --certfile PATH Path to the TLS certificate file to use.
+ -s --skip-verify Don't verify server TLS certificates.
+`
+
+func main() {
+ conf := configure()
+ cl := sliderule.NewClient(conf.clientTLS)
+
+ response, err := cl.Fetch(conf.url.String())
+ if err != nil {
+ fail(err.Error() + "\n")
+ }
+ defer func() {
+ _ = response.Close()
+ _ = conf.output.Close()
+ }()
+
+ _, _ = io.Copy(conf.output, response.Body)
+}
+
+type config struct {
+ verbose bool
+ output io.WriteCloser
+ url *url.URL
+ clientTLS *tls.Config
+}
+
+func configure() config {
+ if len(os.Args) == 1 {
+ fail(usage)
+ }
+
+ conf := config{output: os.Stdout}
+ key := ""
+ cert := ""
+ verify := true
+
+ for i := 1; i <= len(os.Args)-1; i += 1 {
+ switch os.Args[i] {
+ case "-h", "--help":
+ os.Stdout.WriteString(usage)
+ os.Exit(0)
+ case "-v", "--verbose":
+ conf.verbose = true
+ case "-o", "--output":
+ if i+1 == len(os.Args)-1 {
+ fail(usage)
+ }
+
+ out := os.Args[i+1]
+ if out != "-" {
+ output, err := os.OpenFile(out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+ if err != nil {
+ fmt.Println(err.Error())
+ failf("'%s' is not a valid path\n", out)
+ }
+ conf.output = output
+ }
+
+ i += 1
+ case "-k", "--keyfile":
+ if i+1 == len(os.Args)-1 {
+ fail(usage)
+ }
+
+ i += 1
+ key = os.Args[i]
+ case "-c", "--certfile":
+ if i+1 == len(os.Args)-1 {
+ fail(usage)
+ }
+
+ i += 1
+ cert = os.Args[i]
+ case "-s", "--skip-verify":
+ verify = false
+ }
+ }
+
+ conf.clientTLS = &tls.Config{}
+ if key != "" || cert != "" {
+ if key == "" || cert == "" {
+ fail("-k|--keyfile and -c|--certfile must both be present, or neither\n")
+ }
+ tlsConf, err := gemini.FileTLS(cert, key)
+ if err != nil {
+ failf("failed to load TLS key pair")
+ }
+ conf.clientTLS = tlsConf
+ }
+ conf.clientTLS.InsecureSkipVerify = !verify
+
+ u, err := url.Parse(os.Args[len(os.Args)-1])
+ if err != nil || u.Scheme == "" {
+ fail(usage)
+ }
+ conf.url = u
+
+ return conf
+}
+
+func fail(msg string) {
+ os.Stderr.WriteString(msg)
+ os.Exit(1)
+}
+
+func failf(msg string, args ...any) {
+ fmt.Fprintf(os.Stderr, msg, args...)
+ os.Exit(1)
+}