From 93685ba170c63c6509314a72d83e3d2096ed5ab7 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 9 Mar 2021 11:03:01 -0700 Subject: support arxiv, parsing improvements --- http.go | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 http.go (limited to 'http.go') diff --git a/http.go b/http.go new file mode 100644 index 0000000..fcfd38f --- /dev/null +++ b/http.go @@ -0,0 +1,230 @@ +package main + +import ( + "fmt" + "net/http" + "html/template" + "os" + "path/filepath" + "strings" +) + +// IndexHandler renders the index of papers stored in papers.Path +func (papers *Papers) IndexHandler(w http.ResponseWriter, r *http.Request) { + // catch-all for paths unhandled by direct http.HandleFunc calls + if r.URL.Path != "/" { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + t, _ := template.ParseFiles(filepath.Join(templateDir, "layout.html"), + filepath.Join(templateDir, "index.html"), + filepath.Join(templateDir, "list.html"), + ) + res := Resp{ + Papers: papers.List, + } + t.Execute(w, &res) +} + +// AdminHandler renders the index of papers stored in papers.Path with +// additional forms to modify the collection (add, delete, rename...) +func (papers *Papers) AdminHandler(w http.ResponseWriter, r *http.Request) { + t, _ := template.ParseFiles(filepath.Join(templateDir, "admin.html"), + filepath.Join(templateDir, "layout.html"), + filepath.Join(templateDir, "list.html"), + ) + res := Resp{ + Papers: papers.List, + } + if user != "" && pass != "" { + username, password, ok := r.BasicAuth() + if ok && user == username && pass == password { + t.Execute(w, &res) + } else { + w.Header().Add("WWW-Authenticate", + `Basic realm="Please authenticate"`) + http.Error(w, http.StatusText(http.StatusUnauthorized), + http.StatusUnauthorized) + } + } else { + t.Execute(w, &res) + } +} + +// EditHandler renders the index of papers stored in papers.Path, prefixing +// a checkbox to each unique paper and category for modification +func (papers *Papers) EditHandler(w http.ResponseWriter, r *http.Request) { + t, _ := template.ParseFiles(filepath.Join(templateDir, "admin-edit.html"), + filepath.Join(templateDir, "layout.html"), + filepath.Join(templateDir, "list.html"), + ) + res := Resp{ + Papers: papers.List, + } + if user != "" && pass != "" { + username, password, ok := r.BasicAuth() + if !ok || user != username || pass != password { + w.Header().Add("WWW-Authenticate", + `Basic realm="Please authenticate"`) + http.Error(w, http.StatusText(http.StatusUnauthorized), + http.StatusUnauthorized) + return + } + } + if err := r.ParseForm(); err != nil { + res.Status = err.Error() + t.Execute(w, &res) + return + } + + if action := r.FormValue("action"); action == "delete" { + for _, paper := range r.Form["paper"] { + if res.Status != "" { + break + } + if err := papers.DeletePaper(paper); err != nil { + res.Status = err.Error() + } + } + for _, category := range r.Form["category"] { + if res.Status != "" { + break + } + if err := papers.DeleteCategory(category); err != nil { + res.Status = err.Error() + } + } + if res.Status == "" { + res.Status = "delete successful" + } + } else if strings.HasPrefix(action, "move") { + destCategory := strings.SplitN(action, "move-", 2)[1] + for _, paper := range r.Form["paper"] { + if res.Status != "" { + break + } + if err := papers.MovePaper(paper, destCategory); err != nil { + res.Status = err.Error() + } + } + if res.Status == "" { + res.Status = "move successful" + } + } else { + rc := r.FormValue("rename-category") + rt := r.FormValue("rename-to") + if rc != "" && rt != "" { + // ensure filesystem safety of category names + rc = strings.Trim(strings.Replace(rc, "..", "", -1), "/.") + rt = strings.Trim(strings.Replace(rt, "..", "", -1), "/.") + + if err := papers.RenameCategory(rc, rt); err != nil { + res.Status = err.Error() + } + if res.Status == "" { + res.Status = "rename successful" + } + } + } + t.Execute(w, &res) +} + +// AddHandler provides support for new paper processing and category addition +func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) { + t, _ := template.ParseFiles(filepath.Join(templateDir, "admin.html"), + filepath.Join(templateDir, "layout.html"), + filepath.Join(templateDir, "list.html"), + ) + if user != "" && pass != "" { + username, password, ok := r.BasicAuth() + if !ok || user != username || pass != password { + w.Header().Add("WWW-Authenticate", + `Basic realm="Please authenticate"`) + http.Error(w, http.StatusText(http.StatusUnauthorized), + http.StatusUnauthorized) + return + } + } + p := r.FormValue("dl-paper") + c := r.FormValue("dl-category") + nc := r.FormValue("new-category") + + // sanitize input; we use the category to build the path used to save + // papers + nc = strings.Trim(strings.Replace(nc, "..", "", -1), "/.") + res := Resp{Papers: papers.List} + + // paper download, both required fields populated + if len(strings.TrimSpace(p)) > 0 && len(strings.TrimSpace(c)) > 0 { + if paper, err := papers.ProcessAddPaperInput(c, p); err != nil { + res.Status = err.Error() + } else { + if paper.Meta.Title != "" { + res.Status = fmt.Sprintf("%q downloaded successfully", + paper.Meta.Title) + } else { + res.Status = fmt.Sprintf("%q downloaded successfully", + paper.PaperName) + } + res.LastPaperDL = strings.TrimPrefix(paper.PaperPath, + papers.Path+"/") + } + res.LastUsedCategory = c + } else if len(strings.TrimSpace(nc)) > 0 { + // accounts for nested category addition; e.g. "foo/bar/baz" where + // "foo/bar" and/or "foo" do not already exist + n := nc + for n != "." { + _, exists := papers.List[n] + if exists == true { + res.Status = fmt.Sprintf("category %q already exists", n) + } else if err := os.MkdirAll(filepath.Join(papers.Path, n), + os.ModePerm); err != nil { + res.Status = fmt.Sprintf(err.Error()) + } else { + papers.List[n] = make(map[string]*Paper) + } + if res.Status != "" { + break + } + res.LastUsedCategory = n + n = filepath.Dir(n) + } + if res.Status == "" { + res.Status = fmt.Sprintf("category %q added successfully", nc) + } + } + t.Execute(w, &res) +} + +// DownloadHandler serves saved papers up for download +func (papers *Papers) DownloadHandler(w http.ResponseWriter, r *http.Request) { + paper := strings.TrimPrefix(r.URL.Path, "/download/") + category := filepath.Dir(paper) + + // return 404 if the provided paper category or paper key do not exist in + // the papers set + if _, exists := papers.List[category]; exists == false { + http.Error(w, http.StatusText(http.StatusNotFound), + http.StatusNotFound) + return + } + if _, exists := papers.List[category][paper]; exists == false { + http.Error(w, http.StatusText(http.StatusNotFound), + http.StatusNotFound) + return + } + + // ensure the paper (PaperPath) actually exists on the filesystem + i, err := os.Stat(papers.List[category][paper].PaperPath) + if os.IsNotExist(err) { + http.Error(w, http.StatusText(http.StatusNotFound), + http.StatusNotFound) + } else if i.IsDir() { + http.Error(w, http.StatusText(http.StatusForbidden), + http.StatusForbidden) + } else { + http.ServeFile(w, r, papers.List[category][paper].PaperPath) + } +} + -- cgit v1.2.3-54-g00ecf