Index: trunk/cmd/marisa/main.go
===================================================================
--- trunk/cmd/marisa/main.go	(revision 65)
+++ trunk/cmd/marisa/main.go	(revision 67)
@@ -2,10 +2,5 @@
 
 import (
-	"encoding/json"
 	"flag"
-	"fmt"
-	"html/template"
-	"io"
-	"io/ioutil"
 	"log"
 	"net"
@@ -14,13 +9,6 @@
 	"os"
 	"os/signal"
-	"os/user"
-	"path"
-	"path/filepath"
-	"strconv"
 	"syscall"
-	"time"
 
-	"github.com/dustin/go-humanize"
-	"gopkg.in/ini.v1"
 	"marisa.chaotic.ninja/marisa"
 )
@@ -54,274 +42,4 @@
 
 var verbose bool
-
-func writefile(f *os.File, s io.ReadCloser, contentlength int64) error {
-	buffer := make([]byte, 4096)
-	eof := false
-	sz := int64(0)
-
-	defer f.Sync()
-
-	for !eof {
-		n, err := s.Read(buffer)
-		if err != nil && err != io.EOF {
-			return err
-		} else if err == io.EOF {
-			eof = true
-		}
-
-		/* ensure we don't write more than expected */
-		r := int64(n)
-		if sz+r > contentlength {
-			r = contentlength - sz
-			eof = true
-		}
-
-		_, err = f.Write(buffer[:r])
-		if err != nil {
-			return err
-		}
-		sz += r
-	}
-
-	return nil
-}
-
-func writemeta(filename string, expiry int64) error {
-
-	f, _ := os.Open(filename)
-	stat, _ := f.Stat()
-	size := stat.Size()
-	f.Close()
-
-	if expiry < 0 {
-		expiry = conf.expiry
-	}
-
-	meta := metadata{
-		Filename: filepath.Base(filename),
-		Size:     size,
-		Expiry:   time.Now().Unix() + expiry,
-	}
-
-	if verbose {
-		log.Printf("Saving metadata for %s in %s", meta.Filename, conf.metapath+"/"+meta.Filename+".json")
-	}
-
-	f, err := os.Create(conf.metapath + "/" + meta.Filename + ".json")
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-
-	j, err := json.Marshal(meta)
-	if err != nil {
-		return err
-	}
-
-	_, err = f.Write(j)
-
-	return err
-}
-
-func servetemplate(w http.ResponseWriter, f string, d templatedata) {
-	t, err := template.ParseFiles(conf.tmplpath + "/" + f)
-	if err != nil {
-		http.Error(w, "Internal error", http.StatusInternalServerError)
-		return
-	}
-
-	if verbose {
-		log.Printf("Serving template %s", t.Name())
-	}
-
-	err = t.Execute(w, d)
-	if err != nil {
-		fmt.Println(err)
-	}
-}
-
-func uploaderPut(w http.ResponseWriter, r *http.Request) {
-	/* limit upload size */
-	if r.ContentLength > conf.maxsize {
-		http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
-	}
-
-	tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(r.URL.Path))
-	f, err := os.Create(tmp.Name())
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	defer f.Close()
-
-	if verbose {
-		log.Printf("Writing %d bytes to %s", r.ContentLength, tmp.Name())
-	}
-
-	if err = writefile(f, r.Body, r.ContentLength); err != nil {
-		http.Error(w, "Internal error", http.StatusInternalServerError)
-		defer os.Remove(tmp.Name())
-		return
-	}
-	writemeta(tmp.Name(), conf.expiry)
-
-	resp := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
-	w.Write([]byte(resp + "\r\n"))
-}
-
-func uploaderPost(w http.ResponseWriter, r *http.Request) {
-	/* read 32Mb at a time */
-	r.ParseMultipartForm(32 << 20)
-
-	links := []string{}
-	for _, h := range r.MultipartForm.File["file"] {
-		if h.Size > conf.maxsize {
-			http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
-			return
-		}
-
-		post, err := h.Open()
-		if err != nil {
-			http.Error(w, "Internal error", http.StatusInternalServerError)
-			return
-		}
-		defer post.Close()
-
-		tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(h.Filename))
-		f, err := os.Create(tmp.Name())
-		if err != nil {
-			http.Error(w, "Internal error", http.StatusInternalServerError)
-			return
-		}
-		defer f.Close()
-
-		if err = writefile(f, post, h.Size); err != nil {
-			http.Error(w, "Internal error", http.StatusInternalServerError)
-			defer os.Remove(tmp.Name())
-			return
-		}
-
-		expiry, err := strconv.Atoi(r.PostFormValue("expiry"))
-		if err != nil || expiry < 0 {
-			expiry = int(conf.expiry)
-		}
-		writemeta(tmp.Name(), int64(expiry))
-
-		link := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
-		links = append(links, link)
-	}
-
-	switch r.PostFormValue("output") {
-	case "html":
-		data := templatedata{
-			Maxsize: humanize.IBytes(uint64(conf.maxsize)),
-			Links:   links,
-		}
-		servetemplate(w, "/index.html", data)
-	case "json":
-		data, _ := json.Marshal(links)
-		w.Write(data)
-	default:
-		for _, link := range links {
-			w.Write([]byte(link + "\r\n"))
-		}
-	}
-}
-
-func uploaderGet(w http.ResponseWriter, r *http.Request) {
-	// r.URL.Path is sanitized regarding "." and ".."
-	filename := r.URL.Path
-	if r.URL.Path == "/" || r.URL.Path == "/index.html" {
-		data := templatedata{Maxsize: humanize.IBytes(uint64(conf.maxsize))}
-		servetemplate(w, "/index.html", data)
-		return
-	}
-
-	if verbose {
-		log.Printf("Serving file %s", conf.rootdir+filename)
-	}
-
-	http.ServeFile(w, r, conf.rootdir+filename)
-}
-
-func uploaderDelete(w http.ResponseWriter, r *http.Request) {
-	// r.URL.Path is sanitized regarding "." and ".."
-	filename := r.URL.Path
-	filepath := conf.filepath + filename
-
-	if verbose {
-		log.Printf("Deleting file %s", filepath)
-	}
-
-	f, err := os.Open(filepath)
-	if err != nil {
-		http.NotFound(w, r)
-		return
-	}
-	f.Close()
-
-	// Force file expiration
-	writemeta(filepath, 0)
-	w.WriteHeader(http.StatusNoContent)
-}
-
-func uploader(w http.ResponseWriter, r *http.Request) {
-	if verbose {
-		log.Printf("%s: <%s> %s %s %s", r.Host, r.RemoteAddr, r.Method, r.RequestURI, r.Proto)
-	}
-
-	switch r.Method {
-	case "DELETE":
-		uploaderDelete(w, r)
-	case "POST":
-		uploaderPost(w, r)
-	case "PUT":
-		uploaderPut(w, r)
-	case "GET":
-		uploaderGet(w, r)
-	}
-}
-
-func parseconfig(file string) error {
-	cfg, err := ini.Load(file)
-	if err != nil {
-		return err
-	}
-
-	conf.listen = cfg.Section("").Key("listen").String()
-	conf.user = cfg.Section("").Key("user").String()
-	conf.group = cfg.Section("").Key("group").String()
-	conf.baseuri = cfg.Section("").Key("baseuri").String()
-	conf.filepath = cfg.Section("").Key("filepath").String()
-	conf.metapath = cfg.Section("").Key("metapath").String()
-	conf.filectx = cfg.Section("").Key("filectx").String()
-	conf.rootdir = cfg.Section("").Key("rootdir").String()
-	conf.chroot = cfg.Section("").Key("chroot").String()
-	conf.tmplpath = cfg.Section("").Key("tmplpath").String()
-	conf.maxsize, _ = cfg.Section("").Key("maxsize").Int64()
-	conf.expiry, _ = cfg.Section("").Key("expiry").Int64()
-
-	return nil
-}
-
-func usergroupids(username string, groupname string) (int, int, error) {
-	u, err := user.Lookup(username)
-	if err != nil {
-		return -1, -1, err
-	}
-
-	uid, _ := strconv.Atoi(u.Uid)
-	gid, _ := strconv.Atoi(u.Gid)
-
-	if conf.group != "" {
-		g, err := user.LookupGroup(groupname)
-		if err != nil {
-			return uid, -1, err
-		}
-		gid, _ = strconv.Atoi(g.Gid)
-	}
-
-	return uid, gid, nil
-}
 
 func main() {
Index: trunk/cmd/marisa/parseconfig.go
===================================================================
--- trunk/cmd/marisa/parseconfig.go	(revision 67)
+++ trunk/cmd/marisa/parseconfig.go	(revision 67)
@@ -0,0 +1,27 @@
+package main
+
+import (
+	"gopkg.in/ini.v1"
+)
+
+func parseconfig(file string) error {
+        cfg, err := ini.Load(file)
+        if err != nil {
+                return err
+        }
+
+        conf.listen = cfg.Section("marisa").Key("listen").String()
+        conf.user = cfg.Section("marisa").Key("user").String()
+        conf.group = cfg.Section("marisa").Key("group").String()
+        conf.baseuri = cfg.Section("www").Key("baseuri").String()
+        conf.filepath = cfg.Section("www").Key("filepath").String()
+        conf.metapath = cfg.Section("www").Key("metapath").String()
+        conf.filectx = cfg.Section("www").Key("filectx").String()
+        conf.rootdir = cfg.Section("www").Key("rootdir").String()
+        conf.chroot = cfg.Section("marisa").Key("chroot").String()
+        conf.tmplpath = cfg.Section("www").Key("tmplpath").String()
+        conf.maxsize, _ = cfg.Section("www").Key("maxsize").Int64()
+        conf.expiry, _ = cfg.Section("www").Key("expiry").Int64()
+
+        return nil
+}
Index: trunk/cmd/marisa/servetemplate.go
===================================================================
--- trunk/cmd/marisa/servetemplate.go	(revision 67)
+++ trunk/cmd/marisa/servetemplate.go	(revision 67)
@@ -0,0 +1,25 @@
+package main
+
+import (
+	"fmt"
+	"html/template"
+	"log"
+	"net/http"
+)
+
+func servetemplate(w http.ResponseWriter, f string, d templatedata) {
+        t, err := template.ParseFiles(conf.tmplpath + "/" + f)
+        if err != nil {
+                http.Error(w, "Internal error", http.StatusInternalServerError)
+                return
+        }
+
+        if verbose {
+                log.Printf("Serving template %s", t.Name())
+        }
+
+        err = t.Execute(w, d)
+        if err != nil {
+                fmt.Println(err)
+        }
+}
Index: trunk/cmd/marisa/uploader.go
===================================================================
--- trunk/cmd/marisa/uploader.go	(revision 67)
+++ trunk/cmd/marisa/uploader.go	(revision 67)
@@ -0,0 +1,23 @@
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func uploader(w http.ResponseWriter, r *http.Request) {
+        if verbose {
+                log.Printf("%s: <%s> %s %s %s", r.Host, r.RemoteAddr, r.Method, r.RequestURI, r.Proto)
+        }
+
+        switch r.Method {
+        case "DELETE":
+                uploaderDelete(w, r)
+        case "POST":
+                uploaderPost(w, r)
+        case "PUT":
+                uploaderPut(w, r)
+        case "GET":
+                uploaderGet(w, r)
+        }
+}
Index: trunk/cmd/marisa/uploaderdelete.go
===================================================================
--- trunk/cmd/marisa/uploaderdelete.go	(revision 67)
+++ trunk/cmd/marisa/uploaderdelete.go	(revision 67)
@@ -0,0 +1,28 @@
+package main
+
+import (
+	"log"
+	"net/http"
+	"os"
+)
+
+func uploaderDelete(w http.ResponseWriter, r *http.Request) {
+        // r.URL.Path is sanitized regarding "." and ".."
+        filename := r.URL.Path
+        filepath := conf.filepath + filename
+
+        if verbose {
+                log.Printf("Deleting file %s", filepath)
+        }
+
+        f, err := os.Open(filepath)
+        if err != nil {
+                http.NotFound(w, r)
+                return
+        }
+        f.Close()
+
+        // Force file expiration
+        writemeta(filepath, 0)
+        w.WriteHeader(http.StatusNoContent)
+}
Index: trunk/cmd/marisa/uploaderget.go
===================================================================
--- trunk/cmd/marisa/uploaderget.go	(revision 67)
+++ trunk/cmd/marisa/uploaderget.go	(revision 67)
@@ -0,0 +1,24 @@
+package main
+
+import (
+	"log"
+	"net/http"
+
+	"github.com/dustin/go-humanize"
+)
+
+func uploaderGet(w http.ResponseWriter, r *http.Request) {
+        // r.URL.Path is sanitized regarding "." and ".."
+        filename := r.URL.Path
+        if r.URL.Path == "/" || r.URL.Path == "/index.html" {
+                data := templatedata{Maxsize: humanize.IBytes(uint64(conf.maxsize))}
+                servetemplate(w, "/index.html", data)
+                return
+        }
+
+        if verbose {
+                log.Printf("Serving file %s", conf.rootdir+filename)
+        }
+
+        http.ServeFile(w, r, conf.rootdir+filename)
+}
Index: trunk/cmd/marisa/uploaderpost.go
===================================================================
--- trunk/cmd/marisa/uploaderpost.go	(revision 67)
+++ trunk/cmd/marisa/uploaderpost.go	(revision 67)
@@ -0,0 +1,71 @@
+package main
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"strconv"
+
+	"github.com/dustin/go-humanize"
+)
+
+func uploaderPost(w http.ResponseWriter, r *http.Request) {
+        /* read 32Mb at a time */
+        r.ParseMultipartForm(32 << 20)
+
+        links := []string{}
+        for _, h := range r.MultipartForm.File["file"] {
+                if h.Size > conf.maxsize {
+                        http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
+                        return
+                }
+
+                post, err := h.Open()
+                if err != nil {
+                        http.Error(w, "Internal error", http.StatusInternalServerError)
+                        return
+                }
+                defer post.Close()
+
+                tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(h.Filename))
+                f, err := os.Create(tmp.Name())
+                if err != nil {
+                        http.Error(w, "Internal error", http.StatusInternalServerError)
+                        return
+                }
+                defer f.Close()
+
+                if err = writefile(f, post, h.Size); err != nil {
+                        http.Error(w, "Internal error", http.StatusInternalServerError)
+                        defer os.Remove(tmp.Name())
+                        return
+                }
+		 expiry, err := strconv.Atoi(r.PostFormValue("expiry"))
+                if err != nil || expiry < 0 {
+                        expiry = int(conf.expiry)
+                }
+                writemeta(tmp.Name(), int64(expiry))
+
+                link := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
+                links = append(links, link)
+        }
+
+        switch r.PostFormValue("output") {
+        case "html":
+                data := templatedata{
+                        Maxsize: humanize.IBytes(uint64(conf.maxsize)),
+                        Links:   links,
+                }
+                servetemplate(w, "/index.html", data)
+        case "json":
+                data, _ := json.Marshal(links)
+                w.Write(data)
+        default:
+                for _, link := range links {
+                        w.Write([]byte(link + "\r\n"))
+                }
+        }
+}
Index: trunk/cmd/marisa/uploaderput.go
===================================================================
--- trunk/cmd/marisa/uploaderput.go	(revision 67)
+++ trunk/cmd/marisa/uploaderput.go	(revision 67)
@@ -0,0 +1,40 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+)
+
+func uploaderPut(w http.ResponseWriter, r *http.Request) {
+        /* limit upload size */
+        if r.ContentLength > conf.maxsize {
+                http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
+        }
+
+        tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(r.URL.Path))
+        f, err := os.Create(tmp.Name())
+        if err != nil {
+                fmt.Println(err)
+                return
+        }
+        defer f.Close()
+
+        if verbose {
+                log.Printf("Writing %d bytes to %s", r.ContentLength, tmp.Name())
+        }
+
+        if err = writefile(f, r.Body, r.ContentLength); err != nil {
+                http.Error(w, "Internal error", http.StatusInternalServerError)
+                defer os.Remove(tmp.Name())
+                return
+        }
+        writemeta(tmp.Name(), conf.expiry)
+
+        resp := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
+        w.Write([]byte(resp + "\r\n"))
+}
Index: trunk/cmd/marisa/usergroupids.go
===================================================================
--- trunk/cmd/marisa/usergroupids.go	(revision 67)
+++ trunk/cmd/marisa/usergroupids.go	(revision 67)
@@ -0,0 +1,26 @@
+package main
+
+import (
+	"os/user"
+	"strconv"
+)
+
+func usergroupids(username string, groupname string) (int, int, error) {
+        u, err := user.Lookup(username)
+        if err != nil {
+                return -1, -1, err
+        }
+
+        uid, _ := strconv.Atoi(u.Uid)
+        gid, _ := strconv.Atoi(u.Gid)
+
+        if conf.group != "" {
+                g, err := user.LookupGroup(groupname)
+                if err != nil {
+                        return uid, -1, err
+                }
+                gid, _ = strconv.Atoi(g.Gid)
+        }
+
+        return uid, gid, nil
+}
Index: trunk/cmd/marisa/writefile.go
===================================================================
--- trunk/cmd/marisa/writefile.go	(revision 67)
+++ trunk/cmd/marisa/writefile.go	(revision 67)
@@ -0,0 +1,38 @@
+package main
+
+import (
+	"io"
+	"os"
+)
+
+func writefile(f *os.File, s io.ReadCloser, contentlength int64) error {
+        buffer := make([]byte, 4096)
+        eof := false
+        sz := int64(0)
+
+        defer f.Sync()
+
+        for !eof {
+                n, err := s.Read(buffer)
+                if err != nil && err != io.EOF {
+                        return err
+                } else if err == io.EOF {
+                        eof = true
+                }
+
+                /* ensure we don't write more than expected */
+                r := int64(n)
+                if sz+r > contentlength {
+                        r = contentlength - sz
+                        eof = true
+                }
+
+                _, err = f.Write(buffer[:r])
+                if err != nil {
+                        return err
+                }
+                sz += r
+        }
+
+        return nil
+}
Index: trunk/cmd/marisa/writemeta.go
===================================================================
--- trunk/cmd/marisa/writemeta.go	(revision 67)
+++ trunk/cmd/marisa/writemeta.go	(revision 67)
@@ -0,0 +1,46 @@
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+func writemeta(filename string, expiry int64) error {
+
+        f, _ := os.Open(filename)
+        stat, _ := f.Stat()
+        size := stat.Size()
+        f.Close()
+
+        if expiry < 0 {
+                expiry = conf.expiry
+        }
+
+        meta := metadata{
+                Filename: filepath.Base(filename),
+                Size:     size,
+                Expiry:   time.Now().Unix() + expiry,
+        }
+
+        if verbose {
+                log.Printf("Saving metadata for %s in %s", meta.Filename, conf.metapath+"/"+meta.Filename+".json")
+        }
+
+        f, err := os.Create(conf.metapath + "/" + meta.Filename + ".json")
+        if err != nil {
+                return err
+        }
+        defer f.Close()
+
+        j, err := json.Marshal(meta)
+        if err != nil {
+                return err
+        }
+
+	_, err = f.Write(j)
+
+	return err
+}
