From afcb52308470e803a8e940fba6d445823b4be092 Mon Sep 17 00:00:00 2001
From: Sarah Jamie Lewis <sarah@openprivacy.ca>
Date: Sat, 15 Feb 2025 14:49:44 -0800
Subject: [PATCH 1/1] Error When Rejecting Badly Formatted Patch Files / Non
 Patch Files

---
 repo/requests.go                    | 40 ++++++++++++++++++++++-------
 senary.go                           |  2 ++
 static/css/custom.css               |  6 +++++
 templates/request.newpatch.tpl.html |  6 +++++
 4 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/repo/requests.go b/repo/requests.go
index 54adad2..9f520f3 100644
--- a/repo/requests.go
+++ b/repo/requests.go
@@ -24,6 +24,7 @@ type RequestList struct {
 	User     common.AuthInfo
 	Repo     string
 	Requests []*common.IssueRequest
+	Warnings []Warning
 }
 
 type RequestManager struct {
@@ -60,6 +61,11 @@ func NewRequestManager(config *common.Config, repo string, ac *auth.AuthClient)
 	return &RequestManager{config: config, repo: repo, ac: ac, patchHandler: *NewPatchHandler(config, repo)}
 }
 
+func (rm *RequestManager) errorHandler(err string, w http.ResponseWriter, r *http.Request) {
+	r.AddCookie(&http.Cookie{Name: "error", Value: err})
+	rm.Serve(w, r)
+}
+
 func (rm *RequestManager) ApplyPatch(w http.ResponseWriter, r *http.Request) {
 
 	authUser := rm.ac.GetAuthInfo(rm.config, r)
@@ -102,13 +108,19 @@ func (rm *RequestManager) ServePatch(w http.ResponseWriter, r *http.Request) {
 
 func (rm *RequestManager) Serve(w http.ResponseWriter, r *http.Request) {
 
+	baseResponse := RequestList{Repo: rm.repo, User: rm.ac.GetAuthInfo(rm.config, r)}
+
+	if cookie, _ := r.Cookie("error"); cookie != nil {
+		baseResponse.Warnings = append(baseResponse.Warnings, Warning{Level: WARN, Message: cookie.Value})
+	}
+
 	if strings.HasSuffix(r.URL.Path, "/new") {
 		if value := r.FormValue("type"); value == "issue" {
-			rm.config.Templates.ExecuteTemplate(w, "request.new.tpl.html", RequestList{Repo: rm.repo, User: rm.ac.GetAuthInfo(rm.config, r)})
+			rm.config.Templates.ExecuteTemplate(w, "request.new.tpl.html", baseResponse)
 			return
 		}
 		if value := r.FormValue("type"); value == "patch" {
-			rm.config.Templates.ExecuteTemplate(w, "request.newpatch.tpl.html", RequestList{Repo: rm.repo, User: rm.ac.GetAuthInfo(rm.config, r)})
+			rm.config.Templates.ExecuteTemplate(w, "request.newpatch.tpl.html", baseResponse)
 			return
 		}
 	}
@@ -121,7 +133,7 @@ func (rm *RequestManager) Serve(w http.ResponseWriter, r *http.Request) {
 	// list all issues
 	if strings.HasSuffix(r.URL.Path, "requests/") {
 		authuser := rm.ac.GetAuthInfo(rm.config, r)
-		rl := RequestList{Repo: rm.repo, User: authuser}
+		rl := baseResponse
 
 		// Get a list of issues in creation order...
 		issueList := rm.LoadIssues(rm.RequestLogPath(), func(hash string) (*common.IssueRequest, error) {
@@ -152,7 +164,7 @@ func (rm *RequestManager) Serve(w http.ResponseWriter, r *http.Request) {
 	// otherwise list a single issue...
 	authuser := rm.ac.GetAuthInfo(rm.config, r)
 	// otherwise...
-	rt := RequestThread{Repo: rm.repo, User: rm.ac.GetAuthInfo(rm.config, r)}
+	rt := RequestThread{User: baseResponse.User, Repo: baseResponse.Repo, Warnings: baseResponse.Warnings}
 	issuehash := path.Base(r.URL.Path)
 	// Get a list of issues in creation order...
 
@@ -369,6 +381,7 @@ func (rm *RequestManager) updateLog(logpath string, hash string, status common.L
 }
 
 func (rm *RequestManager) handlePatch(w http.ResponseWriter, r *http.Request) {
+
 	// Parse our multipart form, 10 << 20 specifies a maximum
 	// upload of 10 MB files.
 	r.ParseMultipartForm(1024 * 1024 * 200)
@@ -377,20 +390,20 @@ func (rm *RequestManager) handlePatch(w http.ResponseWriter, r *http.Request) {
 	// the Header and the size of the file
 	file, _, err := r.FormFile("patchfile")
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusBadRequest)
+		rm.errorHandler("Could Not Create Patch Request", w, r)
 		return
 	}
 	defer file.Close()
 
 	id, err := common.RandomIdent()
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusBadRequest)
+		rm.errorHandler("Could Not Create Patch Request", w, r)
 		return
 	}
 
 	fileBytes, err := io.ReadAll(file)
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusBadRequest)
+		rm.errorHandler("Could Not Create Patch Request", w, r)
 		return
 	}
 
@@ -398,14 +411,18 @@ func (rm *RequestManager) handlePatch(w http.ResponseWriter, r *http.Request) {
 	buf := bytes.NewBuffer(fileBytes)
 	_, description, err := gitdiff.Parse(buf)
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusBadRequest)
+		rm.errorHandler("Could Not Create Patch Request", w, r)
+		return
+	}
+	if _, err := gitdiff.ParsePatchHeader(description); err != nil {
+		rm.errorHandler("Could Not Create Patch Request", w, r)
 		return
 	}
 
 	summary := r.FormValue("summary")
 	issueRequest, err := common.NewIssueRequest(summary, description, rm.ac.GetAuthInfo(rm.config, r))
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusBadRequest)
+		rm.errorHandler("Could Not Create Patch Request", w, r)
 		return
 	}
 	issueRequest.PatchRef = id
@@ -458,6 +475,11 @@ func (rm *RequestManager) SaveIssue(issueRequest *common.IssueRequest, w http.Re
 }
 
 func (rm *RequestManager) handleNew(w http.ResponseWriter, r *http.Request) {
+	if r.Response != nil {
+		fmt.Printf("now here....\n")
+		rm.Serve(w, r)
+		return
+	}
 
 	summary := r.FormValue("summary")
 	description := r.FormValue("description")
diff --git a/senary.go b/senary.go
index 5060715..c8aa6a1 100644
--- a/senary.go
+++ b/senary.go
@@ -135,6 +135,8 @@ func main() {
 				http.HandleFunc("POST "+path.Join("/repos/", e.Name(), "requests")+"/new", rm.Submit)
 				http.HandleFunc("POST "+path.Join("/repos/", e.Name(), "requests")+"/approve", rm.Submit)
 				http.HandleFunc("POST "+path.Join("/repos/", e.Name(), "requests")+"/tombstone", rm.Submit)
+
+				// allow downloading patches and applying them...
 				http.HandleFunc("GET "+path.Join("/repos/", e.Name(), "patch/{id}/{patch}")+"/{$}", rm.ServePatch)
 				http.HandleFunc("POST "+path.Join("/repos/", e.Name(), "apply/{id}/{patch}")+"/{$}", rm.ApplyPatch)
 				// allow https cloning by redirecting to git https backend
diff --git a/static/css/custom.css b/static/css/custom.css
index 4a70a5a..4c2ba3c 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -72,4 +72,10 @@ mark.warning {
 
 mark.info {
   background-color:var(--pico-color-jade-600);color:var(--pico-color-light);
+}
+
+.error {
+  background-color:var(--pico-color-pink-600);color:var(--pico-color-light);
+  font-weight: bold;
+  text-align: center;
 }
\ No newline at end of file
diff --git a/templates/request.newpatch.tpl.html b/templates/request.newpatch.tpl.html
index d1dfa31..aef5d29 100644
--- a/templates/request.newpatch.tpl.html
+++ b/templates/request.newpatch.tpl.html
@@ -10,6 +10,12 @@
 
 {{template "repomenu.tpl.html" .}}
 <div>
+
+  {{range .Warnings}}
+      <div class="error">{{.Message}}</div>
+      <br/>
+  {{end}}
+
   <h2>Submit a New Change Request</h2>
 <form method="post" enctype="multipart/form-data" >
   <fieldset>
-- 
2.43.0

