aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go167
1 files changed, 167 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..9c3112f
--- /dev/null
+++ b/main.go
@@ -0,0 +1,167 @@
+package main
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/json"
+ "flag"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+var (
+ tls = flag.String("t", ":8443", "[ip]:port to tls-listen to")
+ nontls = flag.String("l", "", "optional, non-tls [ip]:port to listen to")
+ config = flag.String("cfg", "tools.json", "config file with tool-definitions")
+ cert = flag.String("cert", "cert.pem", "certitifate")
+ key = flag.String("key", "key.pem", "key")
+ savedir = flag.String("d", "saved", "direcotry to save the pics.")
+)
+
+type Tool struct {
+ Name string
+ Cmd string
+ Args []string
+ Suffix string
+ Description string
+ Documentation map[string]string
+ Example string
+ BgColor string
+}
+
+var tools = []Tool{}
+
+func (t Tool) execute(in io.Reader, w http.ResponseWriter) {
+ var cmd = exec.Command(t.Cmd, t.Args...) // Call to dot with SVG-output in safe-mode
+ cmd.Stdin = in
+ err, buf := &bytes.Buffer{}, &bytes.Buffer{}
+ cmd.Stderr = err
+ cmd.Stdout = buf
+ if e := cmd.Run(); e == nil {
+ io.Copy(w, buf)
+ } else {
+ log.Printf("%s returned error\n", t.Name)
+ http.Error(w, err.String(), http.StatusBadRequest)
+ }
+}
+
+func (t Tool) compile() func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ t.execute(r.Body, w)
+ }
+}
+
+func (t Tool) svg() func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ name := filepath.Base(r.URL.Path)
+ if file, err := os.Open(filepath.Join(*savedir, name+t.Suffix)); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't open file", http.StatusBadRequest)
+ } else {
+ defer file.Close()
+ t.execute(file, w)
+ }
+ }
+}
+
+const maxSnippetSize = 64 * 1024
+
+func (t Tool) save() func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxSnippetSize))
+ if err != nil {
+ log.Println(err)
+ http.Error(w, "body too large", http.StatusBadRequest)
+ return
+ }
+ r.Body.Close()
+
+ // Create a filename taking the first 10 characters of the sha1-sum of
+ // content. Based on code from the golang-playground.
+ h := sha1.New()
+ io.Copy(h, bytes.NewBuffer(body))
+ sum := h.Sum(nil)
+ b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
+ base64.URLEncoding.Encode(b, sum)
+ name := string(b)[:10]
+
+ // Write snippet to file. TODO: Shall we return error if file exists?
+ if file, err := os.Create(filepath.Join(*savedir, name+t.Suffix)); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't create file", http.StatusInternalServerError)
+ } else if err = file.Chmod(0644); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't setup file", http.StatusInternalServerError)
+ } else if _, err = io.Copy(file, bytes.NewBuffer(body)); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't write file", http.StatusInternalServerError)
+ } else if err = file.Close(); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't close file", http.StatusInternalServerError)
+ }
+ w.Write([]byte(name))
+ }
+}
+
+func (t Tool) load() func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ name := filepath.Base(r.URL.Path)
+ if file, err := os.Open(filepath.Join(*savedir, name+t.Suffix)); err != nil {
+ log.Println(err)
+ http.Error(w, "couldn't open file", http.StatusBadRequest)
+ } else {
+ defer file.Close()
+ io.Copy(w, file)
+ }
+ }
+}
+
+func (t Tool) index() func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if tmpl, err := template.ParseFiles("index.html"); err != nil {
+ log.Printf("error parsing index.html: %v", err)
+ http.Error(w, "internal error", http.StatusInternalServerError)
+ } else if err = tmpl.ExecuteTemplate(w, "index.html", map[string]interface{}{"Tools": tools, "Cur": t}); err != nil {
+ log.Printf("error executing template for %s: %v", t.Name, err)
+ }
+ }
+}
+
+func main() {
+
+ flag.Parse()
+
+ if cfg, err := os.Open(*config); err != nil {
+ log.Fatal(err)
+ } else if err = json.NewDecoder(cfg).Decode(&tools); err != nil {
+ log.Fatalf("error loading %s: %v\n", *config, err)
+ }
+
+ for _, tool := range tools {
+ pre := "/" + tool.Name + "/"
+ http.HandleFunc(pre+"c", tool.compile())
+ http.HandleFunc(pre+"s", tool.save())
+ http.HandleFunc(pre+"l/", tool.load())
+ http.HandleFunc(pre+"svg/", tool.svg())
+ http.HandleFunc(pre, tool.index())
+ log.Println("handler for", pre, "registered")
+ }
+ http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "/"+tools[0].Name, http.StatusFound)
+ })
+
+ if len(*nontls) > 0 {
+ log.Println("listening non-tls on", *nontls)
+ log.Fatal(http.ListenAndServe(*nontls, nil))
+ }
+ log.Println("listening tls on", *tls)
+ log.Fatal(http.ListenAndServeTLS(*tls, *cert, *key, nil))
+}