X-Git-Url: http://jaekl.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=main%2Fhandler.go;h=18366b93dd36d7ac49b2899382572c6e1f3cacdf;hb=723db788dfc2ae83c03ca551908144ec6c47d6b9;hp=52d08e5f8ff98f192f9dbc109418424522ac6719;hpb=d4b5c2903e7b0c2267aa7bfdef514a3d1e447de3;p=quanweb.git diff --git a/main/handler.go b/main/handler.go index 52d08e5..18366b9 100644 --- a/main/handler.go +++ b/main/handler.go @@ -3,7 +3,10 @@ package main import ( "encoding/json" "fmt" + "io" "net/http" + "os" + "path/filepath" "strconv" "strings" ) @@ -12,41 +15,161 @@ const PARAM_IDS = "ids" const MAX_TERMS = 10 // ============================================================================ +func bookMimeType(bookPath string) string { + upper := strings.ToUpper(bookPath) + + if strings.HasSuffix(upper, ".EPUB") { + return "application/epub+zip" + } else if strings.HasSuffix(upper, ".PDF") { + return "application/pdf" + } else { + fmt.Println("Warning: Cannot determine MIME type, will use application/octet-stream:", bookPath) + return "application/octet-stream" + } +} + +func efsPathForId(efsId int) string { + config := GetConfig() + + idStr := fmt.Sprintf("%010d", efsId) + path := fmt.Sprintf("%s/efs/%s/%s/%s/%s/%s.dat", config.basePath, idStr[0:2], idStr[2:4], idStr[4:6], idStr[6:8], idStr) + + return path +} + func handler(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if nil != err { - fmt.Fprintln(w, "ERROR!", err) + handleError(w, err.Error()) return } - action := r.URL.Path[1:] + action := strings.Split(r.URL.Path[1:], "/")[0] switch(action) { + case "book": + handleBook(w, r) + case "download": + handleDownload(w, r) case "info": handleInfo(w, r) case "search": handleSearch(w, r) default: - fmt.Fprintf(w, "Unrecognized request: %s\n", r.URL.Path[1:]) - fmt.Fprintf(w, "id: %s\n", r.FormValue("id")) - - fmt.Fprintln(w, "URL: ", r.URL) - fmt.Fprintln(w, "Query: ", r.URL.Query()) + handleError( + w, + fmt.Sprintf( + "Unrecognized request: %s\nid: %s\nURL: %s\nQuery: %s\n", + r.URL.Path[1:], + r.FormValue("id"), + r.URL, + r.URL.Query())) } } +// Download a book, based on the path stored in the DB +func handleBook(w http.ResponseWriter, r *http.Request) { + fmt.Println("handleBook:", r.URL.Path) + path := r.URL.Path[1:] + tok := strings.Split(path, "/") + if 2 != len(tok) { + handleError(w, fmt.Sprintf("Unexpected path for book download: %s\n", path)) + return + } + + bookId, err := strconv.Atoi(tok[1]) + if (nil != err) || (0 == bookId) { + msg := fmt.Sprintf("Invalid id for book download: \"%s\" (%s)\n", path, err.Error()) + handleError(w, msg) + return + } + + bookPath := queryBookPathById(bookId) + if 0 == len(bookPath) { + handleError(w, fmt.Sprintf("No book for ID: %s\n", bookId)) + return + } + + bookFileName := filepath.Base(bookPath) + + mimeType := bookMimeType(bookPath) + if 0 == len(mimeType) { + handleError(w, fmt.Sprintf("Failed to determine MIME type for book: %s\n", bookPath)) + return + } + + var info os.FileInfo + info, err = os.Stat(bookPath) + if nil != err { + handleError(w, fmt.Sprintf("Failed to read file metadata: \"%s\" (%s)\n", bookPath, err.Error())) + return + } + modTime := info.ModTime() + + var fd *os.File + fd, err = os.Open(bookPath) + if nil != err { + handleError(w, fmt.Sprintf("Failed to open file: \"%s\" (%s)\n", bookPath, err.Error())) + return + } + defer fd.Close() + + // TODO: handle non-ASCII file names. Need to look up the permutations on how to encode that. + w.Header().Set("Content-Disposition", "attachment; filename=" + bookFileName) + w.Header().Set("Content-Type", mimeType) + http.ServeContent(w, r, bookFileName, modTime, fd) +} + +func handleDownload(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path[1:] + tok := strings.Split(path, "/") + if 2 != len(tok) { + fmt.Fprintln(w, "Unexpected path for download:", path) + return + } + efsId, err := strconv.Atoi(tok[1]) + if (nil != err) || (0 == efsId) { + fmt.Fprintln(w, "Invalid id for download:", path, err) + return + } + + mimeType := queryMimeTypeByEfsId(efsId) + if 0 == len(mimeType) { + fmt.Fprintln(w, "No MIME type found for id:", efsId) + return + } + + efsPath := efsPathForId(efsId) + + var fd *os.File + fd, err = os.Open(efsPath) + if nil != err { + fmt.Fprintln(w, "Failed to read file for id:", efsId, err) + return + } + defer fd.Close() + + w.Header().Set("Content-Type", mimeType) + io.Copy(w, fd) +} + +func handleError(w http.ResponseWriter, msg string) { + fmt.Printf("ERROR: %s", msg) + http.Error(w, msg, http.StatusInternalServerError) +} + func handleInfo(w http.ResponseWriter, r *http.Request) { idParams := r.Form[PARAM_IDS] if 1 != len(idParams) { - fmt.Fprintln(w, "ERROR! Detected either zero or multiple ids= parameters. Exactly one expected.") - return - } + handleError(w, "ERROR! Detected either zero or multiple ids= parameters. Exactly one expected.") + return + } idParam := idParams[0] idStrings := strings.Split(idParam, ",") ids := make([]int, len(idStrings)) + var err error for i, v := range(idStrings) { - var err error ids[i], err = strconv.Atoi(v) if nil != err { ids[i] = 0 @@ -56,27 +179,28 @@ func handleInfo(w http.ResponseWriter, r *http.Request) { books := queryBooksByIds(ids) var jsonValue []byte - var err error jsonValue, err = json.Marshal(books) if nil != err { - fmt.Fprintln(w, "ERROR!", err) + handleError(w, err.Error()) } else { w.Write(jsonValue) } } func handleSearch(w http.ResponseWriter, r *http.Request) { - fields := []Field{Author, Title, Series} + var err error + + fields := []Field{Author, Language, List, Series, Title} terms := make([]SearchTerm, len(fields)) - + count := 0 for _, fv := range(fields) { paramName := fv.String() paramValues := r.Form[paramName] for _, pv := range(paramValues) { if count >= len(terms) { - fmt.Printf("WARNING: limit of %v search terms exceeded. One or more terms ignored.") + fmt.Printf("WARNING: limit of %d search terms exceeded. One or more terms ignored.", len(terms)) break } terms[count] = SearchTerm{Attribute:fv, Text:pv} @@ -90,8 +214,9 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { jsonValue, err := json.Marshal(ids) if nil != err { - fmt.Fprintln(w, "ERROR!", err) + handleError(w, err.Error()) } else { w.Write(jsonValue) } } +