Add slider to enable distant paging.
[quanweb.git] / main / handler.go
index 255f19397a6fd6db357bae249a3d882f360e32f7..2d8076e35720c20e5422ecd87fe9eca83f70c7c9 100644 (file)
@@ -6,6 +6,7 @@ import (
   "io"
   "net/http"
   "os"
+  "path/filepath"
   "strconv"
   "strings"
 )
@@ -14,6 +15,19 @@ 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()
 
@@ -33,6 +47,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
   action := strings.Split(r.URL.Path[1:], "/")[0]
 
   switch(action) {
+  case "book":
+    handleBook(w, r)
   case "download":
     handleDownload(w, r)
   case "info":
@@ -48,6 +64,57 @@ func handler(w http.ResponseWriter, r *http.Request) {
   }
 }
 
+// Download a book, based on the path stored in the DB
+func handleBook(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 book download:", path)
+    return
+  }
+
+  bookId, err := strconv.Atoi(tok[1])
+  if (nil != err) || (0 == bookId) {
+    fmt.Fprintln(w, "Invalid id for book download:", path, err)
+    return
+  }
+
+  bookPath := queryBookPathById(bookId)
+  if 0 == len(bookPath) {
+    fmt.Fprintln(w, "No book for ID:", bookId)
+    return
+  }
+
+  bookFileName := filepath.Base(bookPath)
+
+  mimeType := bookMimeType(bookPath)
+  if 0 == len(mimeType) {
+    fmt.Fprintln(w, "Failed to determine MIME type for book:", bookPath)
+    return
+  }
+
+  var info os.FileInfo
+  info, err = os.Stat(bookPath)
+  if nil != err {
+    fmt.Fprintln(w, "Failed to read file metadata:", bookPath, err)
+    return
+  }
+  modTime := info.ModTime()
+  
+  var fd *os.File
+  fd, err = os.Open(bookPath)
+  if nil != err {
+    fmt.Fprintln(w, "Failed to open file:", bookPath, err)
+    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, "/")
@@ -77,6 +144,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request) {
   }
   defer fd.Close()
 
+  w.Header().Set("Content-Type", mimeType)
   io.Copy(w, fd)
 }
 
@@ -90,8 +158,8 @@ func handleInfo(w http.ResponseWriter, r *http.Request) {
   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
@@ -101,7 +169,6 @@ 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)
@@ -111,6 +178,8 @@ func handleInfo(w http.ResponseWriter, r *http.Request) {
 }
 
 func handleSearch(w http.ResponseWriter, r *http.Request) {
+  var err error
+
   fields := []Field{Author, Title, Series}
 
   terms := make([]SearchTerm, len(fields))
@@ -121,7 +190,7 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
     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}
@@ -140,3 +209,4 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
     w.Write(jsonValue)
   }
 }
+