Fix bug in list search that was introduced by sort order work.
[quanweb.git] / main / handler.go
index 8686f2b3a870f051e8a1c096af01db4cde04c79e..bdd6410520896253fc1d68deb76904612adf1ff0 100644 (file)
@@ -6,6 +6,7 @@ import (
   "io"
   "net/http"
   "os"
+  "path/filepath"
   "strconv"
   "strings"
 )
@@ -14,11 +15,24 @@ 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()
+  config := GetConfig()
 
   idStr := fmt.Sprintf("%010d", efsId)
-  path := fmt.Sprintf("%s/%s/%s/%s/%s/%s.dat", config.efsBasePath, idStr[0:2], idStr[2:4], idStr[4:6], idStr[6:8], idStr)
+  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
 }
@@ -26,13 +40,15 @@ func efsPathForId(efsId int) string {
 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 := strings.Split(r.URL.Path[1:], "/")[0]
 
   switch(action) {
+  case "book":
+    handleBook(w, r)
   case "download":
     handleDownload(w, r)
   case "info":
@@ -40,35 +56,69 @@ func handler(w http.ResponseWriter, r *http.Request) {
   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()))
   }
 }
 
-/*
-func handleApp(w http.ResponseWriter, r *http.Request) {
-  fmt.Println("handleApp():", r.URL.Path)
+// 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)
 
-  // Security check:  prevent walking up the directory
-  pos := strings.Index(r.Url.Path, "../")
-  if (-1) == pos {
-    fmt.Fprintln(w, "Paths containing \"../\" are not permitted:", r.URL.Path)
+  mimeType := bookMimeType(bookPath)
+  if 0 == len(mimeType) {
+    handleError(w, fmt.Sprintf("Failed to determine MIME type for book: %s\n", bookPath))
     return
   }
 
-  fileName := "../app" + r.URL.Path
-  _, err := os.Stat(fileName)
-  if nil != err { 
-    fmt.Fprintln(w, "Failed to find file:", fileName, err)
+  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()
 
-  http.ServeFile(w, r, "../app/" + r.URL.Path[1:])
+  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:]
@@ -82,7 +132,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request) {
     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)
@@ -99,15 +149,21 @@ func handleDownload(w http.ResponseWriter, r *http.Request) {
   }
   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, ",")
@@ -125,7 +181,7 @@ func handleInfo(w http.ResponseWriter, r *http.Request) {
   var jsonValue []byte
   jsonValue, err = json.Marshal(books)
   if nil != err {
-    fmt.Fprintln(w, "ERROR!", err)
+    handleError(w, err.Error())
   } else {
     w.Write(jsonValue)
   }
@@ -133,18 +189,16 @@ func handleInfo(w http.ResponseWriter, r *http.Request) {
 
 func handleSearch(w http.ResponseWriter, r *http.Request) {
   var err error
-  fmt.Println("DEBUG:  handleSearch():  " + r.URL.Path)
 
-  fields := []Field{Author, Title, Series}
+  fields := []Field{Author, Language, List, Series, Sort, Title}
 
   terms := make([]SearchTerm, len(fields))
-  
+
   count := 0
   for _, fv := range(fields) {
     paramName := fv.String()
     paramValues := r.Form[paramName]
     for _, pv := range(paramValues) {
-      fmt.Println("DEBUG:  handleSearch():  ", paramName, "=", pv)
       if count >= len(terms) {
         fmt.Printf("WARNING:  limit of %d search terms exceeded.  One or more terms ignored.", len(terms))
         break
@@ -160,7 +214,7 @@ 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)
   }