From 89075959bf7ff1832cc38414a1ae392a49536294 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 07:33:00 +0200
Subject: added simple HTTP upload handler


diff --git a/html/upload-form.html b/html/upload-form.html
new file mode 100644
index 0000000..1718b1b
--- /dev/null
+++ b/html/upload-form.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>rhimportd File Upload</title>
+  </head>
+  <body>
+    <div class="container">
+      <h1>rhimportd File Upload</h1>
+      <form class="form-upload" method="post" enctype="multipart/form-data">
+        <fieldset>
+          <label for="LOGIN_NAME">Username:</label>
+          <input type="text" name="LOGIN_NAME">
+          </br>
+          <label for="LOGIN_NAME">Password:</label>
+          <input type="password" name="PASSWORD">
+          </br>
+          <label for="FILENAME">File:</label>
+          <input type="file" name="FILENAME">
+          </br>
+          <input type="submit" name="submit" value="Submit">
+        </fieldset>
+      </form>
+    </div>
+  </body>
+</html>
diff --git a/src/rhimportd/ctrlWeb.go b/src/rhimportd/ctrlWeb.go
index 40075d0..70d8733 100644
--- a/src/rhimportd/ctrlWeb.go
+++ b/src/rhimportd/ctrlWeb.go
@@ -45,10 +45,11 @@ func (self webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 }
 
 func StartControlWeb(addr string, conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan) {
-	http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler})
 	//	http.Handle("/trusted/simple", webHandler{conf, db, sessions, true, webSimpleHandler})
 
+	http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler})
 	http.Handle("/public/socket", webHandler{conf, db, sessions, false, webSocketHandler})
+	http.Handle("/public/upload", webHandler{conf, db, sessions, false, webUploadHandler})
 
 	rhl.Println("web-ctrl: listening on", addr)
 	server := &http.Server{Addr: addr, ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second}
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
new file mode 100644
index 0000000..02d918c
--- /dev/null
+++ b/src/rhimportd/uploadWeb.go
@@ -0,0 +1,127 @@
+//
+//  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"
+)
+
+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)
+}
+
+func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) {
+	if r.Method == "GET" {
+		http.ServeFile(w, r, "./html/upload-form.html")
+		return
+	}
+
+	if r.Method != "POST" {
+		rhl.Printf("WebUploadHandler: got invalid request method")
+		webUploadErrorResponse(w, http.StatusMethodNotAllowed, "only POST and GET method are allowed")
+		return
+	}
+
+	if err := r.ParseMultipartForm(2 << 30); err != nil { // TODO: howto limit max file size???
+		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()
+
+	rhl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename)
+
+	dstpath, err := ioutil.TempDir(conf.TempDir, "rhimportd-")
+	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()
+
+	io.Copy(dst, src)
+	webUploadResponse(w, "tmp://"+strings.TrimPrefix(dstfile, conf.TempDir))
+}
-- 
cgit v0.10.2


From abc61fa4330ed00a816aa25f9fcd4daeda8a41bd Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 07:51:24 +0200
Subject: limit maximum upload size


diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index 02d918c..e0326ad 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -57,6 +57,10 @@ func webUploadResponse(w http.ResponseWriter, file string) {
 	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.ServeFile(w, r, "./html/upload-form.html")
@@ -69,7 +73,14 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 		return
 	}
 
-	if err := r.ParseMultipartForm(2 << 30); err != nil { // TODO: howto limit max file size???
+	// 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
-- 
cgit v0.10.2


From fe9bf93c2d1d65590695e9739ab30a030a66c3b6 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 07:59:23 +0200
Subject: better name for upload dir


diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index e0326ad..978f6cb 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -63,7 +63,7 @@ const (
 
 func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) {
 	if r.Method == "GET" {
-		http.ServeFile(w, r, "./html/upload-form.html")
+		http.ServeFile(w, r, "./html/upload-form.html") // TODO: hardcoded html-dir... should we print the html directly?
 		return
 	}
 
@@ -117,7 +117,8 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 
 	rhl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename)
 
-	dstpath, err := ioutil.TempDir(conf.TempDir, "rhimportd-")
+	// TODO: uploaded files should be deleted after some time... create directory with timestamp as part of the name?
+	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())
-- 
cgit v0.10.2


From a9e805190242f5580036ff33fe7770af34fc7681 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 15:28:32 +0200
Subject: added cleaner for uploaded files (not done yet)


diff --git a/src/rhimportd/ctrlWeb.go b/src/rhimportd/ctrlWeb.go
index 70d8733..6ea641b 100644
--- a/src/rhimportd/ctrlWeb.go
+++ b/src/rhimportd/ctrlWeb.go
@@ -50,6 +50,7 @@ func StartControlWeb(addr string, conf *rhimport.Config, db *rddb.DBChan, sessio
 	http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler})
 	http.Handle("/public/socket", webHandler{conf, db, sessions, false, webSocketHandler})
 	http.Handle("/public/upload", webHandler{conf, db, sessions, false, webUploadHandler})
+	go webUploadCleaner(conf)
 
 	rhl.Println("web-ctrl: listening on", addr)
 	server := &http.Server{Addr: addr, ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second}
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index 978f6cb..dc9b25c 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -33,6 +33,7 @@ import (
 	"net/http"
 	"os"
 	"strings"
+	"time"
 )
 
 type webUploadResponseData struct {
@@ -87,25 +88,26 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 	}
 
 	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
-	}
+	// TODO: re-add this after testing is done!!!!
+	// 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 {
@@ -137,3 +139,51 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 	io.Copy(dst, src)
 	webUploadResponse(w, "tmp://"+strings.TrimPrefix(dstfile, conf.TempDir))
 }
+
+func webUploadCleanerRun(dir *os.File, conf *rhimport.Config) {
+	t := time.Tick(5 * time.Second)
+	for now := range t {
+		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 {
+			rhdl.Printf("webUploadCleaner: found file/dir '%s', Age: %v", entry.Name(), now.Sub(entry.ModTime()))
+			// TODO: delete file if it is a webupload and it is old enough
+		}
+	}
+}
+
+func webUploadCleaner(conf *rhimport.Config) {
+	for {
+		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
+			}
+		}
+		webUploadCleanerRun(dir, conf)
+		rhdl.Printf("webUploadCleanerRun: returned - restring in 5 sec...")
+		dir.Close()
+
+		time.Sleep(5 * time.Second)
+	}
+}
-- 
cgit v0.10.2


From c69154b5c1e35ef038f00661e66fcb90113891bf Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 15:39:54 +0200
Subject: improved handling of time.Ticker


diff --git a/src/rhimportd/ctrlWatchDir.go b/src/rhimportd/ctrlWatchDir.go
index 1b08e62..14d1c1f 100644
--- a/src/rhimportd/ctrlWatchDir.go
+++ b/src/rhimportd/ctrlWatchDir.go
@@ -165,6 +165,8 @@ func watchDirHandler(conf *rhimport.Config, db *rddb.DBChan, ctx *rhimport.Conte
 
 func watchDirRun(dir *os.File, conf *rhimport.Config, db *rddb.DBChan) {
 	rhl.Printf("watch-dir-ctrl: watching for files in %s", dir.Name())
+	t := time.NewTicker(1 * time.Second)
+	defer t.Stop()
 	for {
 		var err error
 		if _, err = dir.Seek(0, 0); err != nil {
@@ -199,8 +201,7 @@ func watchDirRun(dir *os.File, conf *rhimport.Config, db *rddb.DBChan) {
 				}
 			}
 		}
-
-		time.Sleep(1 * time.Second)
+		<-t.C
 	}
 }
 
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index dc9b25c..1e3e7f0 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -119,7 +119,6 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 
 	rhl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename)
 
-	// TODO: uploaded files should be deleted after some time... create directory with timestamp as part of the name?
 	dstpath, err := ioutil.TempDir(conf.TempDir, "webupload-"+username+"-")
 	if err != nil {
 		rhl.Printf("WebUploadHandler: error creating temporary directory: %v", err)
@@ -141,8 +140,9 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 }
 
 func webUploadCleanerRun(dir *os.File, conf *rhimport.Config) {
-	t := time.Tick(5 * time.Second)
-	for now := range t {
+	t := time.NewTicker(5 * time.Second)
+	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)
@@ -163,7 +163,13 @@ func webUploadCleanerRun(dir *os.File, conf *rhimport.Config) {
 }
 
 func webUploadCleaner(conf *rhimport.Config) {
+	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)
@@ -183,7 +189,5 @@ func webUploadCleaner(conf *rhimport.Config) {
 		webUploadCleanerRun(dir, conf)
 		rhdl.Printf("webUploadCleanerRun: returned - restring in 5 sec...")
 		dir.Close()
-
-		time.Sleep(5 * time.Second)
 	}
 }
-- 
cgit v0.10.2


From 24420034428f6bafc41b34e9b8a6316b4f2aa65e Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 17:35:41 +0200
Subject: upload cleaner works now


diff --git a/src/rhimportd/ctrlWeb.go b/src/rhimportd/ctrlWeb.go
index 6ea641b..6bee325 100644
--- a/src/rhimportd/ctrlWeb.go
+++ b/src/rhimportd/ctrlWeb.go
@@ -44,13 +44,13 @@ func (self webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	self.H(self.Config, self.DBChan, self.SessionStoreChan, self.trusted, w, r)
 }
 
-func StartControlWeb(addr string, conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan) {
+func StartControlWeb(addr string, uploadMaxAge time.Duration, conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan) {
 	//	http.Handle("/trusted/simple", webHandler{conf, db, sessions, true, webSimpleHandler})
 
 	http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler})
 	http.Handle("/public/socket", webHandler{conf, db, sessions, false, webSocketHandler})
 	http.Handle("/public/upload", webHandler{conf, db, sessions, false, webUploadHandler})
-	go webUploadCleaner(conf)
+	go webUploadCleaner(conf, uploadMaxAge)
 
 	rhl.Println("web-ctrl: listening on", addr)
 	server := &http.Server{Addr: addr, ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second}
diff --git a/src/rhimportd/main.go b/src/rhimportd/main.go
index 4b0514b..68b0177 100644
--- a/src/rhimportd/main.go
+++ b/src/rhimportd/main.go
@@ -34,6 +34,7 @@ import (
 	"os"
 	"os/signal"
 	"sync"
+	"time"
 )
 
 var (
@@ -66,6 +67,28 @@ func (s *envStringValue) Get() interface{} { return string(*s) }
 
 func (s *envStringValue) String() string { return fmt.Sprintf("%s", *s) }
 
+type envDurationValue time.Duration
+
+func newEnvDurationValue(key string, dflt time.Duration) (*envDurationValue, error) {
+	if envval, exists := os.LookupEnv(key); exists {
+		var val envDurationValue
+		err := (&val).Set(envval)
+		return &val, err
+	} else {
+		return (*envDurationValue)(&dflt), nil
+	}
+}
+
+func (d *envDurationValue) Set(val string) error {
+	dval, err := time.ParseDuration(val)
+	*d = envDurationValue(dval)
+	return err
+}
+
+func (d *envDurationValue) Get() interface{} { return time.Duration(*d) }
+
+func (d *envDurationValue) String() string { return fmt.Sprintf("%v", *d) }
+
 func main() {
 	webAddr := newEnvStringValue("RHIMPORTD_WEB_ADDR", "localhost:4080")
 	flag.Var(webAddr, "web-addr", "addr:port to listen on (environment: RHIMPORTD_WEB_ADDR)")
@@ -81,6 +104,14 @@ func main() {
 	flag.Var(tempDir, "tmp-dir", "path to temporary files (environment: RHIMPORTD_TEMP_DIR)")
 	localFetchDir := newEnvStringValue("RHIMPORTD_LOCAL_FETCH_DIR", os.TempDir())
 	flag.Var(localFetchDir, "local-fetch-dir", "base path for local:// urls (environment: RHIMPORTD_LOCAL_FETCH_DIR)")
+
+	uploadMaxAge, err := newEnvDurationValue("RHIMPORTD_UPLOAD_MAX_AGE", 30*time.Minute)
+	if err != nil {
+		rhl.Println("Error parsing RHIMPORTD_UPLOAD_MAX_AGE from environment:", err)
+		return
+	}
+	flag.Var(uploadMaxAge, "upload-max-age", "maximum age of uploaded files before the get deleted (environment: RHIMPORTD_UPLOAD_MAX_AGE)")
+
 	help := flag.Bool("help", false, "show usage")
 
 	flag.Parse()
@@ -112,7 +143,7 @@ func main() {
 		go func() {
 			defer wg.Done()
 			rhl.Println("starting web-ctrl")
-			StartControlWeb(webAddr.Get().(string), conf, db.GetInterface(), sessions.GetInterface())
+			StartControlWeb(webAddr.Get().(string), uploadMaxAge.Get().(time.Duration), conf, db.GetInterface(), sessions.GetInterface())
 			rhl.Println("web-ctrl finished")
 		}()
 	}
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index 1e3e7f0..3a4e7bc 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -117,7 +117,7 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 	}
 	defer src.Close()
 
-	rhl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename)
+	rhdl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename)
 
 	dstpath, err := ioutil.TempDir(conf.TempDir, "webupload-"+username+"-")
 	if err != nil {
@@ -135,12 +135,18 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 	}
 	defer dst.Close()
 
-	io.Copy(dst, src)
+	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) {
-	t := time.NewTicker(5 * time.Second)
+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
@@ -156,13 +162,25 @@ func webUploadCleanerRun(dir *os.File, conf *rhimport.Config) {
 		}
 
 		for _, entry := range entries {
-			rhdl.Printf("webUploadCleaner: found file/dir '%s', Age: %v", entry.Name(), now.Sub(entry.ModTime()))
-			// TODO: delete file if it is a webupload and it is old enough
+			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) {
+func webUploadCleaner(conf *rhimport.Config, maxAge time.Duration) {
 	first := true
 	for {
 		if !first {
@@ -186,7 +204,8 @@ func webUploadCleaner(conf *rhimport.Config) {
 				continue
 			}
 		}
-		webUploadCleanerRun(dir, conf)
+		rhdl.Printf("webUploadCleaner: running with max age: %v", maxAge)
+		webUploadCleanerRun(dir, conf, maxAge)
 		rhdl.Printf("webUploadCleanerRun: returned - restring in 5 sec...")
 		dir.Close()
 	}
-- 
cgit v0.10.2


From 8933910f9846bac630bf0dab06a4b7cd6b2b5ce3 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 18:10:36 +0200
Subject: added http static dir for static files


diff --git a/html/upload-form.html b/html/upload-form.html
deleted file mode 100644
index 1718b1b..0000000
--- a/html/upload-form.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>rhimportd File Upload</title>
-  </head>
-  <body>
-    <div class="container">
-      <h1>rhimportd File Upload</h1>
-      <form class="form-upload" method="post" enctype="multipart/form-data">
-        <fieldset>
-          <label for="LOGIN_NAME">Username:</label>
-          <input type="text" name="LOGIN_NAME">
-          </br>
-          <label for="LOGIN_NAME">Password:</label>
-          <input type="password" name="PASSWORD">
-          </br>
-          <label for="FILENAME">File:</label>
-          <input type="file" name="FILENAME">
-          </br>
-          <input type="submit" name="submit" value="Submit">
-        </fieldset>
-      </form>
-    </div>
-  </body>
-</html>
diff --git a/src/rhimportd/ctrlWeb.go b/src/rhimportd/ctrlWeb.go
index 6bee325..2b52ef0 100644
--- a/src/rhimportd/ctrlWeb.go
+++ b/src/rhimportd/ctrlWeb.go
@@ -44,9 +44,10 @@ func (self webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	self.H(self.Config, self.DBChan, self.SessionStoreChan, self.trusted, w, r)
 }
 
-func StartControlWeb(addr string, uploadMaxAge time.Duration, conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan) {
-	//	http.Handle("/trusted/simple", webHandler{conf, db, sessions, true, webSimpleHandler})
+func StartControlWeb(addr, staticDir string, uploadMaxAge time.Duration, conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan) {
+	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
 
+	//	http.Handle("/trusted/simple", webHandler{conf, db, sessions, true, webSimpleHandler})
 	http.Handle("/public/simple", webHandler{conf, db, sessions, false, webSimpleHandler})
 	http.Handle("/public/socket", webHandler{conf, db, sessions, false, webSocketHandler})
 	http.Handle("/public/upload", webHandler{conf, db, sessions, false, webUploadHandler})
diff --git a/src/rhimportd/main.go b/src/rhimportd/main.go
index 68b0177..97a12ec 100644
--- a/src/rhimportd/main.go
+++ b/src/rhimportd/main.go
@@ -92,6 +92,8 @@ func (d *envDurationValue) String() string { return fmt.Sprintf("%v", *d) }
 func main() {
 	webAddr := newEnvStringValue("RHIMPORTD_WEB_ADDR", "localhost:4080")
 	flag.Var(webAddr, "web-addr", "addr:port to listen on (environment: RHIMPORTD_WEB_ADDR)")
+	webStaticDir := newEnvStringValue("RHIMPORTD_WEB_STATIC_DIR", "/usr/share/rhimportd/web-static/")
+	flag.Var(webStaticDir, "web-static-dir", "path to static html files (environment: RHIMPORTD_WEB_STATIC_DIR)")
 	telnetAddr := newEnvStringValue("RHIMPORTD_TELNET_ADDR", "localhost:4023")
 	flag.Var(telnetAddr, "telnet-addr", "addr:port to listen on (environment: RHIMPORTD_TELNET_ADDR)")
 	watchDir := newEnvStringValue("RHIMPORTD_WATCH_DIR", "")
@@ -143,7 +145,7 @@ func main() {
 		go func() {
 			defer wg.Done()
 			rhl.Println("starting web-ctrl")
-			StartControlWeb(webAddr.Get().(string), uploadMaxAge.Get().(time.Duration), conf, db.GetInterface(), sessions.GetInterface())
+			StartControlWeb(webAddr.Get().(string), webStaticDir.Get().(string), uploadMaxAge.Get().(time.Duration), conf, db.GetInterface(), sessions.GetInterface())
 			rhl.Println("web-ctrl finished")
 		}()
 	}
diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index 3a4e7bc..4dc0368 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -64,7 +64,7 @@ const (
 
 func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) {
 	if r.Method == "GET" {
-		http.ServeFile(w, r, "./html/upload-form.html") // TODO: hardcoded html-dir... should we print the html directly?
+		http.Redirect(w, r, "/static/upload-form.html", http.StatusTemporaryRedirect)
 		return
 	}
 
diff --git a/web-static/upload-form.html b/web-static/upload-form.html
new file mode 100644
index 0000000..bbb397c
--- /dev/null
+++ b/web-static/upload-form.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>rhimportd File Upload</title>
+  </head>
+  <body>
+    <div class="container">
+      <h1>rhimportd File Upload</h1>
+      <form class="form-upload" method="post" action="/public/upload" enctype="multipart/form-data">
+        <fieldset>
+          <label for="LOGIN_NAME">Username:</label>
+          <input type="text" name="LOGIN_NAME">
+          </br>
+          <label for="LOGIN_NAME">Password:</label>
+          <input type="password" name="PASSWORD">
+          </br>
+          <label for="FILENAME">File:</label>
+          <input type="file" name="FILENAME">
+          </br>
+          <input type="submit" name="submit" value="Submit">
+        </fieldset>
+      </form>
+    </div>
+  </body>
+</html>
-- 
cgit v0.10.2


From 761d6d0824c0cc92fc746b77499ac563f4e6e579 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Sat, 2 Apr 2016 18:50:06 +0200
Subject: reenable password check for upload


diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go
index 4dc0368..4b56a43 100644
--- a/src/rhimportd/uploadWeb.go
+++ b/src/rhimportd/uploadWeb.go
@@ -88,26 +88,25 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport
 	}
 
 	username := r.FormValue("LOGIN_NAME")
-	// TODO: re-add this after testing is done!!!!
-	// 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
-	// }
+	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 {
-- 
cgit v0.10.2