diff options
author | Christian Pointner <equinox@helsinki.at> | 2016-04-02 16:53:16 (GMT) |
---|---|---|
committer | Christian Pointner <equinox@helsinki.at> | 2016-04-02 16:53:16 (GMT) |
commit | 374cabeb4d2d59b588e9d4a30e3c09e2dbc0abdd (patch) | |
tree | 06bd6587085c6a572bee5077c440b445a4479b6a /src/rhimportd/uploadWeb.go | |
parent | 27eaf84de6c5900c739f73725bd07270d43d49b7 (diff) | |
parent | 761d6d0824c0cc92fc746b77499ac563f4e6e579 (diff) |
Merge branch 'upload-feature'
Diffstat (limited to 'src/rhimportd/uploadWeb.go')
-rw-r--r-- | src/rhimportd/uploadWeb.go | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go new file mode 100644 index 0000000..4b56a43 --- /dev/null +++ b/src/rhimportd/uploadWeb.go @@ -0,0 +1,211 @@ +// +// rhimportd +// +// The Radio Helsinki Rivendell Import Daemon +// +// +// Copyright (C) 2015-2016 Christian Pointner <equinox@helsinki.at> +// +// This file is part of rhimportd. +// +// rhimportd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// rhimportd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with rhimportd. If not, see <http://www.gnu.org/licenses/>. +// + +package main + +import ( + "code.helsinki.at/rhrd-go/rddb" + "code.helsinki.at/rhrd-go/rhimport" + "encoding/json" + "io" + "io/ioutil" + "net/http" + "os" + "strings" + "time" +) + +type webUploadResponseData struct { + ResponseCode int `json:"RESPONSE_CODE"` + ErrorString string `json:"ERROR_STRING"` + SourceFile string `json:"SOURCE_FILE"` +} + +func webUploadErrorResponse(w http.ResponseWriter, code int, errStr string) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + encoder := json.NewEncoder(w) + respdata := webUploadResponseData{code, errStr, ""} + encoder.Encode(respdata) +} + +func webUploadResponse(w http.ResponseWriter, file string) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + encoder := json.NewEncoder(w) + respdata := webUploadResponseData{http.StatusOK, "success", file} + encoder.Encode(respdata) +} + +const ( + webUploadMaxRequestSize = 2 << 30 // 2GB +) + +func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + http.Redirect(w, r, "/static/upload-form.html", http.StatusTemporaryRedirect) + return + } + + if r.Method != "POST" { + rhl.Printf("WebUploadHandler: got invalid request method") + webUploadErrorResponse(w, http.StatusMethodNotAllowed, "only POST and GET method are allowed") + return + } + + // This is from: stackoverflow.com/questions/26392196 + if r.ContentLength > webUploadMaxRequestSize { + rhl.Printf("WebUploadHandler: request ist to large: %d > %d", r.ContentLength, webUploadMaxRequestSize) + webUploadErrorResponse(w, http.StatusExpectationFailed, "request too large") + return + } + r.Body = http.MaxBytesReader(w, r.Body, webUploadMaxRequestSize) + if err := r.ParseMultipartForm(32 << 10); err != nil { + rhl.Printf("WebUploadHandler: error while parsing multi-part-form: %v", err) + webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + username := r.FormValue("LOGIN_NAME") + password := r.FormValue("PASSWORD") + if username == "" { + webUploadErrorResponse(w, http.StatusBadRequest, "missing field LOGIN_NAME") + return + } + if password == "" { + webUploadErrorResponse(w, http.StatusBadRequest, "missing field LOGIN_NAME") + return + } + + if authenticated, err := db.CheckPassword(username, password); err != nil { + rhl.Printf("WebUploadHandler: error checking username/password: %v", err) + webUploadErrorResponse(w, http.StatusUnauthorized, err.Error()) + return + } else if !authenticated { + rhl.Printf("WebUploadHandler: invalid username/password") + webUploadErrorResponse(w, http.StatusUnauthorized, "invalid username/password") + return + } + + src, hdr, err := r.FormFile("FILENAME") + if err != nil { + rhdl.Printf("WebUploadHandler: error looking for upload file in form: %s", err) + webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + defer src.Close() + + rhdl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename) + + dstpath, err := ioutil.TempDir(conf.TempDir, "webupload-"+username+"-") + if err != nil { + rhl.Printf("WebUploadHandler: error creating temporary directory: %v", err) + webUploadErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + dstfile := dstpath + "/" + hdr.Filename + dst, err := os.OpenFile(dstfile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + rhl.Printf("WebUploadHandler: Unable to create file '%s': %v", dstfile, err) + webUploadErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + defer dst.Close() + + size, err := io.Copy(dst, src) + if err != nil { + rhl.Printf("WebUploadHandler: error storing uploaded file '%s': %v", dstfile, err) + webUploadErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + rhl.Printf("WebUploadHandler: stored file '%s' (size: %d Bytes)", dstfile, size) + webUploadResponse(w, "tmp://"+strings.TrimPrefix(dstfile, conf.TempDir)) +} + +func webUploadCleanerRun(dir *os.File, conf *rhimport.Config, maxAge time.Duration) { + t := time.NewTicker(1 * time.Minute) + defer t.Stop() + for now := range t.C { + var err error + if _, err = dir.Seek(0, 0); err != nil { + rhl.Printf("webUploadCleaner: reading directory contents failed: %s", err) + return + } + + var entries []os.FileInfo + if entries, err = dir.Readdir(0); err != nil { + rhl.Printf("webUploadCleaner: reading directory contents failed: %s", err) + return + } + + for _, entry := range entries { + if !strings.HasPrefix(entry.Name(), "webupload-") { + continue + } + + age := now.Sub(entry.ModTime()) + if age <= maxAge { + continue + } + + if err := os.RemoveAll(conf.TempDir + entry.Name()); err != nil { + rhl.Printf("webUploadCleaner: deleting dir '%s' failed: %v", entry.Name(), err) + continue + } + rhl.Printf("webUploadCleaner: successfully deleted dir '%s', Age: %v", entry.Name(), age) + } + } +} + +func webUploadCleaner(conf *rhimport.Config, maxAge time.Duration) { + first := true + for { + if !first { + time.Sleep(5 * time.Second) + } + first = false + + dir, err := os.Open(conf.TempDir) + if err != nil { + rhl.Printf("webUploadCleaner: %s", err) + continue + } + if i, err := dir.Stat(); err != nil { + rhl.Printf("webUploadCleaner: %s", err) + dir.Close() + continue + } else { + if !i.IsDir() { + rhl.Printf("webUploadCleaner: %s is not a directory", conf.TempDir) + dir.Close() + continue + } + } + rhdl.Printf("webUploadCleaner: running with max age: %v", maxAge) + webUploadCleanerRun(dir, conf, maxAge) + rhdl.Printf("webUploadCleanerRun: returned - restring in 5 sec...") + dir.Close() + } +} |