package trantor import ( log "github.com/cihub/seelog" "crypto/rand" "crypto/sha256" "encoding/base64" "io" "mime/multipart" "net/http" "github.com/meskio/epubgo" "gitlab.com/trantor/trantor/lib/database" "gitlab.com/trantor/trantor/lib/parser" "gitlab.com/trantor/trantor/lib/storage" ) const ( uploadChanSize = 100 ) func InitUpload(database database.DB, store storage.Store) { uploadChannel = make(chan uploadRequest, uploadChanSize) go uploadWorker(database, store) } var uploadChannel chan uploadRequest type uploadRequest struct { file multipart.File header *multipart.FileHeader id int } func uploadWorker(database database.DB, store storage.Store) { for req := range uploadChannel { req.processFile(database, store) } } func (req uploadRequest) processFile(db database.DB, store storage.Store) { defer req.file.Close() h := sha256.New() if _, err := io.Copy(h, req.file); err != nil { log.Warn("Can't read uploaded file ", req.header.Filename, ": ", err) db.UpdateSubmission(req.id, "There was a problem with the uploaded book, try again later.", nil) return } fileHash := h.Sum(nil) if db.ExistsBookHash(fileHash) { log.Info("Uploaded file ", req.header.Filename, " already in the library.") db.UpdateSubmission(req.id, "The uploaded book is already in the library (it could be in the moderation queue)", nil) return } epub, err := epubgo.Load(req.file, req.header.Size) if err != nil { log.Warn("Not valid epub uploaded file ", req.header.Filename, ": ", err) db.UpdateSubmission(req.id, "It is not a valid epub file", nil) return } defer epub.Close() book := parser.EpubMetadata(epub) book.ID = GenID() req.file.Seek(0, 0) size, err := store.Store(book.ID, req.file, epubFile) if err != nil { log.Error("Error storing book (", book.ID, "): ", err) db.UpdateSubmission(req.id, "There was a problem in our server", nil) return } book.FileSize = int(size) book.FileHash = fileHash book.Cover = GetCover(epub, book.ID, store) err = db.AddBook(book) if err != nil { log.Error("Error storing metadata (", book.ID, "): ", err) db.UpdateSubmission(req.id, "There was a problem in our server", nil) return } log.Info("File uploaded: ", req.header.Filename) db.UpdateSubmission(req.id, "Waiting for moderation", &book) } func uploadPostHandler(h handler) { const _2M int64 = (1 << 20) * 2 if h.ro { h.sess.Notify("Upload failed!", "The library is in Read Only mode, no books can be uploaded", "danger") uploadHandler(h) return } if err := h.r.ParseMultipartForm(_2M); nil != err { log.Error("Can't parse form: ", err) return } defer h.r.MultipartForm.RemoveAll() filesForm := h.r.MultipartForm.File["epub"] submissionID := GenID() for _, f := range filesForm { submission := database.Submission{ SubmissionID: submissionID, Filename: f.Filename, Status: "Waiting to be processed, reload the page in few minutes", } file, err := f.Open() if err != nil { log.Error("Can not open uploaded file ", f.Filename, ": ", err) h.sess.Notify("Upload problem!", "There was a problem with book "+f.Filename, "danger") submission.Status = "It was not possible to read the book" h.db.AddSubmission(submission, h.sess.User) continue } id, err := h.db.AddSubmission(submission, h.sess.User) if err != nil { log.Error("Can add submission to db for ", f.Filename, ": ", err) } uploadChannel <- uploadRequest{file, f, id} } http.Redirect(h.w, h.r, "/submission/"+submissionID, http.StatusFound) } func uploadHandler(h handler) { var data struct{ S Status } data.S = GetStatus(h) data.S.Title = "Upload -- " + data.S.Title data.S.Upload = true h.load("upload", data) } func GenID() string { b := make([]byte, 12) rand.Read(b) return base64.URLEncoding.EncodeToString(b) }