commit 2dbe318b187f9d96a205a4908b7f042ff53b0ffb Author: andreimarcu Date: Thu Sep 24 01:44:49 2015 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b3418c --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +Linx Server +====== +Soon-to-be opensource replacement of Linx + +**Please do not use yet! Consider it in pre-alpha development stages.** + +Build & Run +---------------- + +1. ```go get -u github.com/andreimarcu/linx-server ``` +2. ```cd $GOPATH/src/github.com/andreimarcu/linx-server ``` +3. ```go build && ./linx-server``` + +By default linx will bind to ```http://127.0.0.1:8080/```, use the "files/" files directory and the "linx" sitename. +Configurable flags can be found in ```server.go```. + + +TODO +-------- +Please refer to the [main TODO issue](https://github.com/andreimarcu/linx-server/issues/1) + +Author +------- +Andrei Marcu, http://andreim.net/ \ No newline at end of file diff --git a/display.go b/display.go new file mode 100644 index 0000000..4e236f9 --- /dev/null +++ b/display.go @@ -0,0 +1,54 @@ +package main + +import ( + "net/http" + "os" + "path" + "strings" + + "github.com/flosch/pongo2" + "github.com/rakyll/magicmime" + "github.com/zenazn/goji/web" +) + +func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) { + filename := c.URLParams["name"] + absPath := path.Join(Config.filesDir, filename) + fileInfo, err := os.Stat(absPath) + + if os.IsNotExist(err) { + http.Error(w, http.StatusText(404), 404) + return + } + + if err := magicmime.Open(magicmime.MAGIC_MIME_TYPE | + magicmime.MAGIC_SYMLINK | + magicmime.MAGIC_ERROR); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + defer magicmime.Close() + + mimetype, err := magicmime.TypeByFile(absPath) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + var tpl *pongo2.Template + + if strings.HasPrefix(mimetype, "image/") { + tpl = pongo2.Must(pongo2.FromCache("templates/display/image.html")) + } else { + tpl = pongo2.Must(pongo2.FromCache("templates/display/file.html")) + } + + err = tpl.ExecuteWriter(pongo2.Context{ + "mime": mimetype, + "sitename": Config.siteName, + "filename": filename, + "size": fileInfo.Size(), + }, w) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} diff --git a/pages.go b/pages.go new file mode 100644 index 0000000..50d0e1e --- /dev/null +++ b/pages.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/flosch/pongo2" + "net/http" + + "github.com/zenazn/goji/web" +) + +func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) { + indexTpl := pongo2.Must(pongo2.FromCache("templates/index.html")) + + err := indexTpl.ExecuteWriter(pongo2.Context{"sitename": Config.siteName}, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..b63910b --- /dev/null +++ b/server.go @@ -0,0 +1,47 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net" + "net/http" + "regexp" + + "github.com/zenazn/goji" + // "github.com/zenazn/goji/web" +) + +var Config struct { + bind string + filesDir string + siteName string +} + +func main() { + flag.StringVar(&Config.bind, "b", "127.0.0.1:8080", + "host to bind to (default: 127.0.0.1:8080)") + flag.StringVar(&Config.filesDir, "d", "files/", + "path to files directory (default: files/)") + flag.StringVar(&Config.siteName, "n", "linx", + "name of the site") + flag.Parse() + + fmt.Printf("About to listen on http://%s\n", Config.bind) + + nameRe := regexp.MustCompile(`^/(?P[a-z0-9-\.]+)$`) + selifRe := regexp.MustCompile(`^/selif/(?P[a-z0-9-\.]+)$`) + + goji.Get("/", indexHandler) + goji.Post("/upload", uploadPostHandler) + goji.Put("/upload", uploadPutHandler) + goji.Get(nameRe, fileDisplayHandler) + goji.Handle(selifRe, http.StripPrefix("/selif/", http.FileServer(http.Dir(Config.filesDir)))) + + listener, err := net.Listen("tcp", Config.bind) + if err != nil { + log.Fatal("Could not bind: ", err) + } + + goji.ServeListener(listener) +} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..34e960c --- /dev/null +++ b/templates/base.html @@ -0,0 +1,13 @@ + + + {% block title %}{{ sitename }}{% endblock %} + + + +
+ {% block content %}{% endblock %} +
+ + + + \ No newline at end of file diff --git a/templates/display/file.html b/templates/display/file.html new file mode 100644 index 0000000..a5d33a3 --- /dev/null +++ b/templates/display/file.html @@ -0,0 +1,6 @@ +{% extends "../base.html" %} + +{% block content %} +

Viewing file {{ filename }} of mime: {{ mime }}

+

Click to download

+{% endblock %} diff --git a/templates/display/image.html b/templates/display/image.html new file mode 100644 index 0000000..440ea4b --- /dev/null +++ b/templates/display/image.html @@ -0,0 +1,6 @@ +{% extends "../base.html" %} + +{% block content %} +

Viewing file {{ filename }}

+ +{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..568f158 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +
+ + +
+{% endblock %} diff --git a/upload.go b/upload.go new file mode 100644 index 0000000..8a5016e --- /dev/null +++ b/upload.go @@ -0,0 +1,118 @@ +package main + +import ( + "errors" + "fmt" + "io" + "net/http" + "os" + "path" + "regexp" + "strings" + + "code.google.com/p/go-uuid/uuid" + "github.com/zenazn/goji/web" +) + +type UploadRequest struct { + src io.Reader + filename string + expiry int + randomBarename bool +} + +type Upload struct { + Filename string + Size int64 + Expiry int +} + +func uploadPostHandler(c web.C, w http.ResponseWriter, r *http.Request) { + upReq := UploadRequest{} + + file, headers, err := r.FormFile("file") + if err != nil { + fmt.Fprintf(w, err.Error()) + return + } + defer file.Close() + + upReq.src = file + upReq.filename = headers.Filename + + upload, err := processUpload(upReq) + if err != nil { + fmt.Fprintf(w, "Failed to upload: %v", err) + return + } + + fmt.Fprintf(w, "File %s uploaded successfully.", upload.Filename) +} + +func uploadPutHandler(c web.C, w http.ResponseWriter, r *http.Request) { + upReq := UploadRequest{} + + defer r.Body.Close() + upReq.src = r.Body + + upload, err := processUpload(upReq) + if err != nil { + fmt.Fprintf(w, "Failed to upload") + return + } + + fmt.Fprintf(w, "File %s uploaded successfully.", upload.Filename) +} + +func processUpload(upReq UploadRequest) (upload Upload, err error) { + barename, extension := barePlusExt(upReq.filename) + + if upReq.randomBarename || len(barename) == 0 { + barename = generateBarename() + } + + if len(extension) == 0 { + extension = "ext" + } + + upload.Filename = strings.Join([]string{barename, extension}, ".") + + dst, ferr := os.Create(path.Join("files/", upload.Filename)) + defer dst.Close() + if ferr != nil { + err = ferr + return + } + + bytes, cerr := io.Copy(dst, upReq.src) + if cerr != nil { + err = cerr + return + } else if bytes == 0 { + err = errors.New("Empty file") + return + } + + upload.Size = bytes + + return +} + +func generateBarename() string { + return uuid.New()[:8] +} + +func barePlusExt(filename string) (barename, extension string) { + re := regexp.MustCompile(`[^A-Za-z0-9\-]`) + + filename = strings.TrimSpace(filename) + filename = strings.ToLower(filename) + + extension = path.Ext(filename) + barename = filename[:len(filename)-len(extension)] + + extension = re.ReplaceAllString(extension, "") + barename = re.ReplaceAllString(barename, "") + + return +}