Merge pull request #102 from mutantmonkey/maxexpiry
Add option for maximum expiration time (fixes #99)
This commit is contained in:
commit
29d3157a03
|
@ -45,6 +45,7 @@ allowhotlink = true
|
|||
- ```-filespath files/"``` -- Path to store uploads (default is files/)
|
||||
- ```-metapath meta/``` -- Path to store information about uploads (default is meta/)
|
||||
- ```-maxsize 4294967296``` -- maximum upload file size in bytes (default 4GB)
|
||||
- ```-maxexpiry 86400``` -- maximum expiration time in seconds (default is 0, which is no expiry)
|
||||
- ```-allowhotlink``` -- Allow file hotlinking
|
||||
- ```-contentsecuritypolicy "..."``` -- Content-Security-Policy header for pages (default is "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; referrer origin;")
|
||||
- ```-filecontentsecuritypolicy "..."``` -- Content-Security-Policy header for files (default is "default-src 'none'; img-src 'self'; object-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; referrer origin;")
|
||||
|
|
53
expiry.go
53
expiry.go
|
@ -2,8 +2,25 @@ package main
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
var defaultExpiryList = []uint64{
|
||||
60,
|
||||
300,
|
||||
3600,
|
||||
86400,
|
||||
604800,
|
||||
2419200,
|
||||
31536000,
|
||||
}
|
||||
|
||||
type ExpirationTime struct {
|
||||
Seconds uint64
|
||||
Human string
|
||||
}
|
||||
|
||||
var neverExpire = time.Unix(0, 0)
|
||||
|
||||
// Determine if a file with expiry set to "ts" has expired yet
|
||||
|
@ -21,3 +38,39 @@ func isFileExpired(filename string) (bool, error) {
|
|||
|
||||
return isTsExpired(metadata.Expiry), nil
|
||||
}
|
||||
|
||||
// Return a list of expiration times and their humanized versions
|
||||
func listExpirationTimes() []ExpirationTime {
|
||||
epoch := time.Now()
|
||||
actualExpiryInList := false
|
||||
var expiryList []ExpirationTime
|
||||
|
||||
for _, expiry := range defaultExpiryList {
|
||||
if Config.maxExpiry == 0 || expiry <= Config.maxExpiry {
|
||||
if expiry == Config.maxExpiry {
|
||||
actualExpiryInList = true
|
||||
}
|
||||
|
||||
duration := time.Duration(expiry) * time.Second
|
||||
expiryList = append(expiryList, ExpirationTime{
|
||||
expiry,
|
||||
humanize.RelTime(epoch, epoch.Add(duration), "", ""),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if Config.maxExpiry == 0 {
|
||||
expiryList = append(expiryList, ExpirationTime{
|
||||
0,
|
||||
"never",
|
||||
})
|
||||
} else if actualExpiryInList == false {
|
||||
duration := time.Duration(Config.maxExpiry) * time.Second
|
||||
expiryList = append(expiryList, ExpirationTime{
|
||||
Config.maxExpiry,
|
||||
humanize.RelTime(epoch, epoch.Add(duration), "", ""),
|
||||
})
|
||||
}
|
||||
|
||||
return expiryList
|
||||
}
|
||||
|
|
5
pages.go
5
pages.go
|
@ -22,6 +22,7 @@ const (
|
|||
func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||
err := renderTemplate(Templates["index.html"], pongo2.Context{
|
||||
"maxsize": Config.maxSize,
|
||||
"expirylist": listExpirationTimes(),
|
||||
}, r, w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -29,7 +30,9 @@ func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func pasteHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||
err := renderTemplate(Templates["paste.html"], pongo2.Context{}, r, w)
|
||||
err := renderTemplate(Templates["paste.html"], pongo2.Context{
|
||||
"expirylist": listExpirationTimes(),
|
||||
}, r, w)
|
||||
if err != nil {
|
||||
oopsHandler(c, w, r, RespHTML, "")
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ var Config struct {
|
|||
fileContentSecurityPolicy string
|
||||
xFrameOptions string
|
||||
maxSize int64
|
||||
maxExpiry uint64
|
||||
realIp bool
|
||||
noLogs bool
|
||||
allowHotlink bool
|
||||
|
@ -211,6 +212,8 @@ func main() {
|
|||
"site base url (including trailing slash)")
|
||||
flag.Int64Var(&Config.maxSize, "maxsize", 4*1024*1024*1024,
|
||||
"maximum upload file size in bytes (default 4GB)")
|
||||
flag.Uint64Var(&Config.maxExpiry, "maxexpiry", 0,
|
||||
"maximum expiration time in seconds (default is 0, which is no expiry)")
|
||||
flag.StringVar(&Config.certFile, "certfile", "",
|
||||
"path to ssl certificate (for https)")
|
||||
flag.StringVar(&Config.keyFile, "keyfile", "",
|
||||
|
|
|
@ -54,6 +54,44 @@ func TestIndex(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIndexStandardMaxExpiry(t *testing.T) {
|
||||
mux := setup()
|
||||
Config.maxExpiry = 60
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if strings.Contains(w.Body.String(), ">1 hour</object>") {
|
||||
t.Fatal("String '>1 hour</object>' found in index response")
|
||||
}
|
||||
|
||||
Config.maxExpiry = 0
|
||||
}
|
||||
|
||||
func TestIndexWeirdMaxExpiry(t *testing.T) {
|
||||
mux := setup()
|
||||
Config.maxExpiry = 1500
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if strings.Contains(w.Body.String(), ">never</object>") {
|
||||
t.Fatal("String '>never</object>' found in index response")
|
||||
}
|
||||
|
||||
Config.maxExpiry = 0
|
||||
}
|
||||
|
||||
func TestAddHeader(t *testing.T) {
|
||||
Config.addHeaders = []string{"Linx-Test: It works!"}
|
||||
|
||||
|
@ -408,6 +446,62 @@ func TestPostJSONUpload(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPostJSONUploadMaxExpiry(t *testing.T) {
|
||||
mux := setup()
|
||||
Config.maxExpiry = 300
|
||||
|
||||
testExpiries := []string{"86400", "-150"}
|
||||
for _, expiry := range testExpiries {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
filename := generateBarename() + ".txt"
|
||||
|
||||
var b bytes.Buffer
|
||||
mw := multipart.NewWriter(&b)
|
||||
fw, err := mw.CreateFormFile("file", filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fw.Write([]byte("File content"))
|
||||
mw.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", "/upload/", &b)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Linx-Expiry", expiry)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != 200 {
|
||||
t.Log(w.Body.String())
|
||||
t.Fatalf("Status code is not 200, but %d", w.Code)
|
||||
}
|
||||
|
||||
var myjson RespOkJSON
|
||||
err = json.Unmarshal([]byte(w.Body.String()), &myjson)
|
||||
if err != nil {
|
||||
fmt.Println(w.Body.String())
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
myExp, err := strconv.ParseInt(myjson.Expiry, 10, 64)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := time.Now().Add(time.Duration(Config.maxExpiry) * time.Second).Unix()
|
||||
if myExp != expected {
|
||||
t.Fatalf("File expiry is not %d but %s", expected, myjson.Expiry)
|
||||
}
|
||||
}
|
||||
|
||||
Config.maxExpiry = 0
|
||||
}
|
||||
|
||||
func TestPostExpiresJSONUpload(t *testing.T) {
|
||||
mux := setup()
|
||||
w := httptest.NewRecorder()
|
||||
|
|
|
@ -20,14 +20,9 @@
|
|||
<div id="expiry">
|
||||
<label>File expiry:
|
||||
<select name="expires" id="expires">
|
||||
<option value="0">never</option>
|
||||
<option value="60">a minute</option>
|
||||
<option value="300">5 minutes</option>
|
||||
<option value="3600">an hour</option>
|
||||
<option value="86400">a day</option>
|
||||
<option value="604800">a week</option>
|
||||
<option value="2419200">a month</option>
|
||||
<option value="29030400">a year</option>
|
||||
{% for expiry in expirylist %}
|
||||
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -8,19 +8,13 @@
|
|||
|
||||
<div class="right">
|
||||
<select id="expiry" name="expires">
|
||||
<option disabled=disabled>Expires:</option>
|
||||
<option value="0">never</option>
|
||||
<option value="60">a minute</option>
|
||||
<option value="300">5 minutes</option>
|
||||
<option value="3600">an hour</option>
|
||||
<option value="86400">a day</option>
|
||||
<option value="604800">a week</option>
|
||||
<option value="2419200">a month</option>
|
||||
<option value="29030400">a year</option>
|
||||
<option disabled="disabled">Expires:</option>
|
||||
{% for expiry in expirylist %}
|
||||
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<input type="submit" value="Paste">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -343,12 +343,15 @@ func barePlusExt(filename string) (barename, extension string) {
|
|||
|
||||
func parseExpiry(expStr string) time.Duration {
|
||||
if expStr == "" {
|
||||
return 0
|
||||
return time.Duration(Config.maxExpiry) * time.Second
|
||||
} else {
|
||||
expiry, err := strconv.ParseInt(expStr, 10, 64)
|
||||
expiry, err := strconv.ParseUint(expStr, 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
return time.Duration(Config.maxExpiry) * time.Second
|
||||
} else {
|
||||
if Config.maxExpiry > 0 && expiry > Config.maxExpiry {
|
||||
expiry = Config.maxExpiry
|
||||
}
|
||||
return time.Duration(expiry) * time.Second
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue