summaryrefslogtreecommitdiff
path: root/src/rhimportd/uploadWeb.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/rhimportd/uploadWeb.go')
-rw-r--r--src/rhimportd/uploadWeb.go211
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()
+ }
+}