forked from tangled.org/core
this repo has no description
1package knotserver 2 3import ( 4 "compress/gzip" 5 "fmt" 6 "io" 7 "net/http" 8 "path/filepath" 9 10 securejoin "github.com/cyphar/filepath-securejoin" 11 "github.com/go-chi/chi/v5" 12 "tangled.sh/tangled.sh/core/knotserver/git/service" 13) 14 15func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) { 16 did := chi.URLParam(r, "did") 17 name := chi.URLParam(r, "name") 18 repoName, err := securejoin.SecureJoin(did, name) 19 if err != nil { 20 gitError(w, "repository not found", http.StatusNotFound) 21 d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 22 return 23 } 24 25 repoPath, err := securejoin.SecureJoin(d.c.Repo.ScanPath, repoName) 26 if err != nil { 27 gitError(w, "repository not found", http.StatusNotFound) 28 d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 29 return 30 } 31 32 cmd := service.ServiceCommand{ 33 Dir: repoPath, 34 Stdout: w, 35 } 36 37 serviceName := r.URL.Query().Get("service") 38 switch serviceName { 39 case "git-upload-pack": 40 w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") 41 w.WriteHeader(http.StatusOK) 42 43 if err := cmd.InfoRefs(); err != nil { 44 gitError(w, err.Error(), http.StatusInternalServerError) 45 d.l.Error("git: process failed", "handler", "InfoRefs", "service", serviceName, "error", err) 46 return 47 } 48 case "git-receive-pack": 49 d.RejectPush(w, r, name) 50 default: 51 gitError(w, fmt.Sprintf("service unsupported: '%s'", serviceName), http.StatusForbidden) 52 } 53} 54 55func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) { 56 did := chi.URLParam(r, "did") 57 name := chi.URLParam(r, "name") 58 repo, err := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name)) 59 if err != nil { 60 writeError(w, err.Error(), 500) 61 d.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err) 62 return 63 } 64 65 var bodyReader io.ReadCloser = r.Body 66 if r.Header.Get("Content-Encoding") == "gzip" { 67 gzipReader, err := gzip.NewReader(r.Body) 68 if err != nil { 69 writeError(w, err.Error(), 500) 70 d.l.Error("git: failed to create gzip reader", "handler", "UploadPack", "error", err) 71 return 72 } 73 defer gzipReader.Close() 74 bodyReader = gzipReader 75 } 76 77 w.Header().Set("Content-Type", "application/x-git-upload-pack-result") 78 w.Header().Set("Connection", "Keep-Alive") 79 80 d.l.Info("git: executing git-upload-pack", "handler", "UploadPack", "repo", repo) 81 82 cmd := service.ServiceCommand{ 83 Dir: repo, 84 Stdout: w, 85 Stdin: bodyReader, 86 } 87 88 w.WriteHeader(http.StatusOK) 89 90 if err := cmd.UploadPack(); err != nil { 91 d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err) 92 return 93 } 94} 95 96func (d *Handle) ReceivePack(w http.ResponseWriter, r *http.Request) { 97 did := chi.URLParam(r, "did") 98 name := chi.URLParam(r, "name") 99 _, err := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name)) 100 if err != nil { 101 gitError(w, err.Error(), http.StatusForbidden) 102 d.l.Error("git: failed to secure join repo path", "handler", "ReceivePack", "error", err) 103 return 104 } 105 106 d.RejectPush(w, r, name) 107} 108 109func (d *Handle) RejectPush(w http.ResponseWriter, r *http.Request, unqualifiedRepoName string) { 110 // A text/plain response will cause git to print each line of the body 111 // prefixed with "remote: ". 112 w.Header().Set("content-type", "text/plain; charset=UTF-8") 113 w.WriteHeader(http.StatusForbidden) 114 115 fmt.Fprintf(w, "Welcome to Tangled.sh!\n\nPushes are currently only supported over SSH.") 116 fmt.Fprintf(w, "\n\n") 117} 118 119func gitError(w http.ResponseWriter, msg string, status int) { 120 w.Header().Set("content-type", "text/plain; charset=UTF-8") 121 w.WriteHeader(status) 122 fmt.Fprintf(w, "%s\n", msg) 123}