-
+{% block content %}
+
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/torrent_test.go b/torrent_test.go
index 1d227fd..f14c613 100644
--- a/torrent_test.go
+++ b/torrent_test.go
@@ -68,7 +68,7 @@ func TestCreateTorrentWithImage(t *testing.T) {
bencode.DecodeBytes(encoded, &decoded)
- if decoded.Info.Pieces != "r\x01\x80j\x99\x84\n\xd3dZ;1NX\xec;\x9d$+f" {
+ if decoded.Info.Pieces != "\xd6\xff\xbf'^)\x85?\xb4.\xb0\xc1|\xa3\x83\xeeX\xf9\xfd\xd7" {
t.Fatal("Torrent pieces did not match expected pieces for image")
}
}
diff --git a/upload.go b/upload.go
index 6533bfe..0485c22 100644
--- a/upload.go
+++ b/upload.go
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
+ "log"
"net/http"
"net/url"
"path"
@@ -15,13 +16,13 @@ import (
"strings"
"time"
+ "github.com/andreimarcu/linx-server/auth/apikeys"
"github.com/andreimarcu/linx-server/backends"
"github.com/andreimarcu/linx-server/expiry"
"github.com/dchest/uniuri"
+ "github.com/gabriel-vasile/mimetype"
"github.com/zenazn/goji/web"
- "gopkg.in/h2non/filetype.v1"
)
-
var FileTooLargeError = errors.New("File too large.")
var fileBlacklist = map[string]bool{
"favicon.ico": true,
@@ -40,6 +41,8 @@ type UploadRequest struct {
expiry time.Duration // Seconds until expiry, 0 = never
deleteKey string // Empty string if not defined
randomBarename bool
+ accessKey string // Empty string if not defined
+ srcIp string // Empty string if not defined
}
// Metadata associated with a file as it would actually be stored
@@ -58,7 +61,6 @@ func uploadPostHandler(c web.C, w http.ResponseWriter, r *http.Request) {
uploadHeaderProcess(r, &upReq)
contentType := r.Header.Get("Content-Type")
-
if strings.HasPrefix(contentType, "multipart/form-data") {
file, headers, err := r.FormFile("file")
if err != nil {
@@ -88,11 +90,11 @@ func uploadPostHandler(c web.C, w http.ResponseWriter, r *http.Request) {
}
upReq.expiry = parseExpiry(r.PostFormValue("expires"))
-
+ upReq.accessKey = r.PostFormValue(accessKeyParamName)
if r.PostFormValue("randomize") == "true" {
upReq.randomBarename = true
}
-
+ upReq.srcIp = r.Header.Get("X-Forwarded-For")
upload, err := processUpload(upReq)
if strings.EqualFold("application/json", r.Header.Get("Accept")) {
@@ -123,11 +125,11 @@ func uploadPostHandler(c web.C, w http.ResponseWriter, r *http.Request) {
func uploadPutHandler(c web.C, w http.ResponseWriter, r *http.Request) {
upReq := UploadRequest{}
uploadHeaderProcess(r, &upReq)
-
+
defer r.Body.Close()
upReq.filename = c.URLParams["name"]
upReq.src = http.MaxBytesReader(w, r.Body, Config.maxSize)
-
+ upReq.srcIp = r.Header.Get("X-Forwarded-For")
upload, err := processUpload(upReq)
if strings.EqualFold("application/json", r.Header.Get("Accept")) {
@@ -157,8 +159,22 @@ func uploadPutHandler(c web.C, w http.ResponseWriter, r *http.Request) {
func uploadRemote(c web.C, w http.ResponseWriter, r *http.Request) {
if Config.remoteAuthFile != "" {
- result, err := checkAuth(remoteAuthKeys, r.FormValue("key"))
+ key := r.FormValue("key")
+ if key == "" && Config.basicAuth {
+ _, password, ok := r.BasicAuth()
+ if ok {
+ key = password
+ }
+ }
+ result, err := apikeys.CheckAuth(remoteAuthKeys, key)
if err != nil || !result {
+ if Config.basicAuth {
+ rs := ""
+ if Config.siteName != "" {
+ rs = fmt.Sprintf(` realm="%s"`, Config.siteName)
+ }
+ w.Header().Set("WWW-Authenticate", `Basic`+rs)
+ }
unauthorizedHandler(c, w, r)
return
}
@@ -171,19 +187,21 @@ func uploadRemote(c web.C, w http.ResponseWriter, r *http.Request) {
upReq := UploadRequest{}
grabUrl, _ := url.Parse(r.FormValue("url"))
+ directURL := r.FormValue("direct_url") == "yes"
resp, err := http.Get(grabUrl.String())
if err != nil {
oopsHandler(c, w, r, RespAUTO, "Could not retrieve URL")
return
}
-
+
upReq.filename = filepath.Base(grabUrl.Path)
upReq.src = http.MaxBytesReader(w, resp.Body, Config.maxSize)
upReq.deleteKey = r.FormValue("deletekey")
+ upReq.accessKey = r.FormValue(accessKeyParamName)
upReq.randomBarename = r.FormValue("randomize") == "yes"
upReq.expiry = parseExpiry(r.FormValue("expiry"))
-
+ upReq.srcIp = r.Header.Get("X-Forwarded-For")
upload, err := processUpload(upReq)
if strings.EqualFold("application/json", r.Header.Get("Accept")) {
@@ -201,17 +219,22 @@ func uploadRemote(c web.C, w http.ResponseWriter, r *http.Request) {
return
}
- http.Redirect(w, r, Config.sitePath+upload.Filename, 303)
+ if directURL {
+ http.Redirect(w, r, Config.sitePath+Config.selifPath+upload.Filename, 303)
+ } else {
+ http.Redirect(w, r, Config.sitePath+upload.Filename, 303)
+ }
}
}
func uploadHeaderProcess(r *http.Request, upReq *UploadRequest) {
if r.Header.Get("Linx-Randomize") == "yes" {
upReq.randomBarename = true
+ } else {
+ upReq.randomBarename = false
}
-
upReq.deleteKey = r.Header.Get("Linx-Delete-Key")
-
+ upReq.accessKey = r.Header.Get(accessKeyHeaderName)
// Get seconds until expiry. Non-integer responses never expire.
expStr := r.Header.Get("Linx-Expiry")
upReq.expiry = parseExpiry(expStr)
@@ -243,11 +266,11 @@ func processUpload(upReq UploadRequest) (upload Upload, err error) {
header = header[:n]
// Determine the type of file from header
- kind, err := filetype.Match(header)
- if err != nil || kind.Extension == "unknown" {
+ kind := mimetype.Detect(header)
+ if len(kind.Extension()) < 2 {
extension = "file"
} else {
- extension = kind.Extension
+ extension = kind.Extension()[1:] // remove leading "."
}
}
@@ -298,19 +321,45 @@ func processUpload(upReq UploadRequest) (upload Upload, err error) {
return upload, errors.New("Prohibited filename")
}
+ // Lock the upload
+ log.Printf("Lock %s", upload.Filename)
+ err = storageBackend.Lock(upload.Filename)
+ if err != nil {
+ return upload, err
+ }
+
// Get the rest of the metadata needed for storage
var fileExpiry time.Time
+ maxDurationTime := time.Duration(Config.maxDurationTime) * time.Second
if upReq.expiry == 0 {
- fileExpiry = expiry.NeverExpire
+ if upReq.size > Config.maxDurationSize && maxDurationTime > 0 {
+ fileExpiry = time.Now().Add(maxDurationTime)
+ } else {
+ fileExpiry = expiry.NeverExpire
+ }
} else {
- fileExpiry = time.Now().Add(upReq.expiry)
+ if upReq.size > Config.maxDurationSize && upReq.expiry > maxDurationTime {
+ fileExpiry = time.Now().Add(maxDurationTime)
+ } else {
+ fileExpiry = time.Now().Add(upReq.expiry)
+ }
}
if upReq.deleteKey == "" {
upReq.deleteKey = uniuri.NewLen(30)
}
+ if Config.disableAccessKey == true {
+ upReq.accessKey = ""
+ }
+ log.Printf("Write %s", upload.Filename)
+ upload.Metadata, err = storageBackend.Put(upload.Filename, io.MultiReader(bytes.NewReader(header), upReq.src), fileExpiry, upReq.deleteKey, upReq.accessKey, upReq.srcIp)
+ if err != nil {
+ return upload, err
+ }
- upload.Metadata, err = storageBackend.Put(upload.Filename, io.MultiReader(bytes.NewReader(header), upReq.src), fileExpiry, upReq.deleteKey)
+ // Unlock the upload
+ log.Printf("Unlock %s", upload.Filename)
+ err = storageBackend.Unlock(upload.Filename)
if err != nil {
return upload, err
}
@@ -328,6 +377,7 @@ func generateJSONresponse(upload Upload, r *http.Request) []byte {
"direct_url": getSiteURL(r) + Config.selifPath + upload.Filename,
"filename": upload.Filename,
"delete_key": upload.Metadata.DeleteKey,
+ "access_key": upload.Metadata.AccessKey,
"expiry": strconv.FormatInt(upload.Metadata.Expiry.Unix(), 10),
"size": strconv.FormatInt(upload.Metadata.Size, 10),
"mimetype": upload.Metadata.Mimetype,
@@ -373,11 +423,11 @@ func barePlusExt(filename string) (barename, extension string) {
func parseExpiry(expStr string) time.Duration {
if expStr == "" {
- return time.Duration(Config.maxExpiry) * time.Second
+ return time.Duration(Config.defaultExpiry) * time.Second
} else {
fileExpiry, err := strconv.ParseUint(expStr, 10, 64)
if err != nil {
- return time.Duration(Config.maxExpiry) * time.Second
+ return time.Duration(Config.defaultExpiry) * time.Second
} else {
if Config.maxExpiry > 0 && (fileExpiry > Config.maxExpiry || fileExpiry == 0) {
fileExpiry = Config.maxExpiry
diff --git a/util.go b/util.go
index 3074ebc..dabe227 100644
--- a/util.go
+++ b/util.go
@@ -1,15 +1,10 @@
package main
-func extensionToHlAndAceLangs(extension string) (hlExt, aceExt string) {
+func extensionToHlLang(extension string) (hlExt string) {
hlExt, exists := extensionToHl[extension]
if !exists {
hlExt = "text"
}
-
- aceExt, exists = extensionToAce[extension]
- if !exists {
- aceExt = "text"
- }
return
}
@@ -18,83 +13,63 @@ func supportedBinExtension(extension string) bool {
return exists
}
-var extensionToAce = map[string]string{
- "c": "c_cpp",
- "h": "c_cpp",
- "cpp": "c_cpp",
- "clj": "clojure",
- "coffee": "coffee",
- "cfc": "coldfusion",
- "cs": "csharp",
- "sh": "sh",
- "bash": "sh",
- "css": "css",
- "go": "golang",
- "diff": "diff",
- "html": "html",
- "xml": "xml",
- "ini": "ini",
- "java": "java",
- "js": "javascript",
- "json": "json",
- "jsp": "jsp",
- "tex": "latex",
- "lisp": "lisp",
- "less": "less",
- "lua": "lua",
- "md": "markdown",
- "ocaml": "ocaml",
- "tcl": "tcl",
- "yaml": "yaml",
- "php": "php",
- "pl": "perl",
- "py": "python",
- "rb": "ruby",
- "sql": "sql",
- "apache": "apache",
- "cmake": "cmake",
- "bat": "dos",
- "scala": "scala",
- "txt": "text",
-}
-
var extensionToHl = map[string]string{
- "c": "cpp",
- "h": "cpp",
- "cpp": "c_cpp",
- "clj": "clojure",
- "coffee": "coffee",
- "cfc": "coldfusion",
- "cs": "csharp",
- "sh": "sh",
- "bash": "sh",
- "css": "css",
- "go": "go",
- "diff": "diff",
- "html": "html",
- "htm": "html",
- "ini": "ini",
- "java": "java",
- "js": "javascript",
- "json": "json",
- "jsp": "jsp",
- "tex": "latex",
- "lisp": "lisp",
- "less": "less",
- "lua": "lua",
- "ocaml": "ocaml",
- "tcl": "tcl",
- "nginx": "nginx",
- "xml": "xml",
- "yaml": "yaml",
- "php": "php",
- "pl": "perl",
- "py": "python",
- "rb": "ruby",
- "sql": "sql",
- "apache": "apache",
- "cmake": "cmake",
- "bat": "dos",
- "scala": "scala",
- "txt": "text",
+ "ahk": "autohotkey",
+ "apache": "apache",
+ "applescript": "applescript",
+ "bas": "basic",
+ "bash": "sh",
+ "bat": "dos",
+ "c": "cpp",
+ "cfc": "coldfusion",
+ "clj": "clojure",
+ "cmake": "cmake",
+ "coffee": "coffee",
+ "cpp": "c_cpp",
+ "cs": "csharp",
+ "css": "css",
+ "d": "d",
+ "dart": "dart",
+ "diff": "diff",
+ "dockerfile": "dockerfile",
+ "elm": "elm",
+ "erl": "erlang",
+ "for": "fortran",
+ "go": "go",
+ "h": "cpp",
+ "htm": "html",
+ "html": "html",
+ "ini": "ini",
+ "java": "java",
+ "js": "javascript",
+ "json": "json",
+ "jsp": "jsp",
+ "kt": "kotlin",
+ "less": "less",
+ "lisp": "lisp",
+ "lua": "lua",
+ "m": "objectivec",
+ "nginx": "nginx",
+ "ocaml": "ocaml",
+ "php": "php",
+ "pl": "perl",
+ "proto": "protobuf",
+ "ps": "powershell",
+ "py": "python",
+ "rb": "ruby",
+ "rs": "rust",
+ "scala": "scala",
+ "scm": "scheme",
+ "scpt": "applescript",
+ "scss": "scss",
+ "sh": "sh",
+ "sql": "sql",
+ "tcl": "tcl",
+ "tex": "latex",
+ "toml": "ini",
+ "ts": "typescript",
+ "txt": "text",
+ "xml": "xml",
+ "yaml": "yaml",
+ "yml": "yaml",
}