parent
a79bc1898a
commit
8f3108148b
|
@ -52,6 +52,7 @@ allowhotlink = true
|
||||||
- ```-xframeoptions "..." ``` -- X-Frame-Options header (default is "SAMEORIGIN")
|
- ```-xframeoptions "..." ``` -- X-Frame-Options header (default is "SAMEORIGIN")
|
||||||
- ```-remoteuploads``` -- (optionally) enable remote uploads (/upload?url=https://...)
|
- ```-remoteuploads``` -- (optionally) enable remote uploads (/upload?url=https://...)
|
||||||
- ```-nologs``` -- (optionally) disable request logs in stdout
|
- ```-nologs``` -- (optionally) disable request logs in stdout
|
||||||
|
- ```-force-random-filename``` -- (optionally) force the use of random filenames
|
||||||
|
|
||||||
#### Storage backends
|
#### Storage backends
|
||||||
The following storage backends are available:
|
The following storage backends are available:
|
||||||
|
|
3
pages.go
3
pages.go
|
@ -23,6 +23,7 @@ func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||||
err := renderTemplate(Templates["index.html"], pongo2.Context{
|
err := renderTemplate(Templates["index.html"], pongo2.Context{
|
||||||
"maxsize": Config.maxSize,
|
"maxsize": Config.maxSize,
|
||||||
"expirylist": listExpirationTimes(),
|
"expirylist": listExpirationTimes(),
|
||||||
|
"forcerandom": Config.forceRandomFilename,
|
||||||
}, r, w)
|
}, r, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
@ -32,6 +33,7 @@ func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||||
func pasteHandler(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{
|
err := renderTemplate(Templates["paste.html"], pongo2.Context{
|
||||||
"expirylist": listExpirationTimes(),
|
"expirylist": listExpirationTimes(),
|
||||||
|
"forcerandom": Config.forceRandomFilename,
|
||||||
}, r, w)
|
}, r, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oopsHandler(c, w, r, RespHTML, "")
|
oopsHandler(c, w, r, RespHTML, "")
|
||||||
|
@ -41,6 +43,7 @@ func pasteHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||||
func apiDocHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
func apiDocHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||||
err := renderTemplate(Templates["API.html"], pongo2.Context{
|
err := renderTemplate(Templates["API.html"], pongo2.Context{
|
||||||
"siteurl": getSiteURL(r),
|
"siteurl": getSiteURL(r),
|
||||||
|
"forcerandom": Config.forceRandomFilename,
|
||||||
}, r, w)
|
}, r, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oopsHandler(c, w, r, RespHTML, "")
|
oopsHandler(c, w, r, RespHTML, "")
|
||||||
|
|
|
@ -65,6 +65,7 @@ var Config struct {
|
||||||
s3Region string
|
s3Region string
|
||||||
s3Bucket string
|
s3Bucket string
|
||||||
s3ForcePathStyle bool
|
s3ForcePathStyle bool
|
||||||
|
forceRandomFilename bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var Templates = make(map[string]*pongo2.Template)
|
var Templates = make(map[string]*pongo2.Template)
|
||||||
|
@ -268,6 +269,8 @@ func main() {
|
||||||
"S3 bucket to use for files and metadata")
|
"S3 bucket to use for files and metadata")
|
||||||
flag.BoolVar(&Config.s3ForcePathStyle, "s3-force-path-style", false,
|
flag.BoolVar(&Config.s3ForcePathStyle, "s3-force-path-style", false,
|
||||||
"Force path-style addressing for S3 (e.g. https://s3.amazonaws.com/linx/example.txt)")
|
"Force path-style addressing for S3 (e.g. https://s3.amazonaws.com/linx/example.txt)")
|
||||||
|
flag.BoolVar(&Config.forceRandomFilename, "force-random-filename", false,
|
||||||
|
"Force all uploads to use a random filename")
|
||||||
|
|
||||||
iniflags.Parse()
|
iniflags.Parse()
|
||||||
|
|
||||||
|
|
|
@ -763,6 +763,32 @@ func TestPutRandomizedUpload(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPutForceRandomUpload(t *testing.T) {
|
||||||
|
mux := setup()
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
oldFRF := Config.forceRandomFilename
|
||||||
|
Config.forceRandomFilename = true
|
||||||
|
filename := "randomizeme.file"
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", "/upload/"+filename, strings.NewReader("File content"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// while this should also work without this header, let's try to force
|
||||||
|
// the randomized filename off to be sure
|
||||||
|
req.Header.Set("Linx-Randomize", "no")
|
||||||
|
|
||||||
|
mux.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Body.String() == Config.siteURL+filename {
|
||||||
|
t.Fatal("Filename was not random")
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.forceRandomFilename = oldFRF
|
||||||
|
}
|
||||||
|
|
||||||
func TestPutNoExtensionUpload(t *testing.T) {
|
func TestPutNoExtensionUpload(t *testing.T) {
|
||||||
mux := setup()
|
mux := setup()
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
@ -1013,6 +1039,55 @@ func TestPutAndOverwrite(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPutAndOverwriteForceRandom(t *testing.T) {
|
||||||
|
var myjson RespOkJSON
|
||||||
|
|
||||||
|
mux := setup()
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
oldFRF := Config.forceRandomFilename
|
||||||
|
Config.forceRandomFilename = true
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", "/upload", strings.NewReader("File content"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
mux.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(w.Body.String()), &myjson)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite it
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req, err = http.NewRequest("PUT", "/upload/"+myjson.Filename, strings.NewReader("New file content"))
|
||||||
|
req.Header.Set("Linx-Delete-Key", myjson.Delete_Key)
|
||||||
|
mux.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != 200 {
|
||||||
|
t.Fatal("Status code was not 200, but " + strconv.Itoa(w.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure it's the new file
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req, err = http.NewRequest("GET", "/"+Config.selifPath+myjson.Filename, nil)
|
||||||
|
mux.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code == 404 {
|
||||||
|
t.Fatal("Status code was 404")
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Body.String() != "New file content" {
|
||||||
|
t.Fatal("File did not contain 'New file content")
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.forceRandomFilename = oldFRF
|
||||||
|
}
|
||||||
|
|
||||||
func TestPutAndSpecificDelete(t *testing.T) {
|
func TestPutAndSpecificDelete(t *testing.T) {
|
||||||
var myjson RespOkJSON
|
var myjson RespOkJSON
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,10 @@ Dropzone.options.dropzone = {
|
||||||
file.uploadElement.setAttribute("style", 'background-image: -webkit-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -moz-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -ms-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -o-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%)');
|
file.uploadElement.setAttribute("style", 'background-image: -webkit-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -moz-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -ms-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: -o-linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%); background-image: linear-gradient(left, #F2F4F7 ' + p + '%, #E2E2E2 ' + p + '%)');
|
||||||
},
|
},
|
||||||
sending: function(file, xhr, formData) {
|
sending: function(file, xhr, formData) {
|
||||||
formData.append("randomize", document.getElementById("randomize").checked);
|
var randomize = document.getElementById("randomize");
|
||||||
|
if(randomize != null) {
|
||||||
|
formData.append("randomize", randomize.checked);
|
||||||
|
}
|
||||||
formData.append("expires", document.getElementById("expires").value);
|
formData.append("expires", document.getElementById("expires").value);
|
||||||
},
|
},
|
||||||
success: function(file, resp) {
|
success: function(file, resp) {
|
||||||
|
|
|
@ -25,8 +25,10 @@
|
||||||
|
|
||||||
<p><strong>Optional headers with the request</strong></p>
|
<p><strong>Optional headers with the request</strong></p>
|
||||||
|
|
||||||
|
{% if not forcerandom %}
|
||||||
<p>Randomize the filename<br/>
|
<p>Randomize the filename<br/>
|
||||||
<code>Linx-Randomize: yes</code></p>
|
<code>Linx-Randomize: yes</code></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<p>Specify a custom deletion key<br/>
|
<p>Specify a custom deletion key<br/>
|
||||||
<code>Linx-Delete-Key: mysecret</code></p>
|
<code>Linx-Delete-Key: mysecret</code></p>
|
||||||
|
@ -56,30 +58,30 @@
|
||||||
|
|
||||||
{% if using_auth %}
|
{% if using_auth %}
|
||||||
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{{ siteurl }}myphoto.jpg</code></pre>
|
{{ siteurl }}{% if not forcerandom %}myphoto.jpg{% else %}7z4h4ut.jpg{% endif %}</code></pre>
|
||||||
{% else %}
|
{% else %}
|
||||||
<pre><code>$ curl -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{{ siteurl }}myphoto.jpg</code></pre>
|
{{ siteurl }}{% if not forcerandom %}myphoto.jpg{% else %}wtq7pan.jpg{% endif %}</code></pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>Uploading myphoto.jpg with an expiry of 20 minutes</p>
|
<p>Uploading myphoto.jpg with an expiry of 20 minutes</p>
|
||||||
|
|
||||||
{% if using_auth %}
|
{% if using_auth %}
|
||||||
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -H "Linx-Expiry: 1200" -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -H "Linx-Expiry: 1200" -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{{ siteurl }}myphoto.jpg</code></pre>
|
{{ siteurl }}{% if not forcerandom %}myphoto.jpg{% else %}jm295snf.jpg{% endif %}</code></pre>
|
||||||
{% else %}
|
{% else %}
|
||||||
<pre><code>$ curl -H "Linx-Expiry: 1200" -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -H "Linx-Expiry: 1200" -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{{ siteurl }}myphoto.jpg</code></pre>
|
{{ siteurl }}{% if not forcerandom %}myphoto.jpg{% else %}1doym9u2.jpg{% endif %}</code></pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>Uploading myphoto.jpg with a random filename and getting a json response:</p>
|
<p>Uploading myphoto.jpg with a random filename and getting a json response:</p>
|
||||||
|
|
||||||
{% if using_auth %}
|
{% if using_auth %}
|
||||||
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -H "Accept: application/json" -H "Linx-Randomize: yes" -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -H "Linx-Api-Key: mysecretkey" -H "Accept: application/json"{% if not forcerandom %} -H "Linx-Randomize: yes"{% endif %} -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{"delete_key":"...","expiry":"0","filename":"f34h4iu.jpg","mimetype":"image/jpeg",
|
{"delete_key":"...","expiry":"0","filename":"f34h4iu.jpg","mimetype":"image/jpeg",
|
||||||
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
||||||
{% else %}
|
{% else %}
|
||||||
<pre><code>$ curl -H "Accept: application/json" -H "Linx-Randomize: yes" -T myphoto.jpg {{ siteurl }}upload/
|
<pre><code>$ curl -H "Accept: application/json"{% if not forcerandom %} -H "Linx-Randomize: yes"{% endif %} -T myphoto.jpg {{ siteurl }}upload/
|
||||||
{"delete_key":"...","expiry":"0","filename":"f34h4iu.jpg","mimetype":"image/jpeg",
|
{"delete_key":"...","expiry":"0","filename":"f34h4iu.jpg","mimetype":"image/jpeg",
|
||||||
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="choices">
|
<div id="choices">
|
||||||
<label><input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename</label>
|
<label>{% if not forcerandom %}<input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename{% endif %}</label>
|
||||||
<div id="expiry">
|
<div id="expiry">
|
||||||
<label>File expiry:
|
<label>File expiry:
|
||||||
<select name="expires" id="expires">
|
<select name="expires" id="expires">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div id="main" class="paste">
|
<div id="main" class="paste">
|
||||||
<div id="info">
|
<div id="info">
|
||||||
<div>
|
<div>
|
||||||
<span class="hint--top hint--bounce" data-hint="Leave empty for random filename"><input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename" /></span>.<span class="hint--top hint--bounce" data-hint="Enable syntax highlighting by adding the extension"><input id="extension" class="codebox" name='extension' type='text' value="" placeholder="txt" /></span>
|
{% if not forcerandom %}<span class="hint--top hint--bounce" data-hint="Leave empty for random filename"><input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename" /></span>{% endif %}.<span class="hint--top hint--bounce" data-hint="Enable syntax highlighting by adding the extension"><input id="extension" class="codebox" name='extension' type='text' value="" placeholder="txt" /></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="submit" value="Paste">
|
<input type="submit" value="Paste">
|
||||||
|
|
21
upload.go
21
upload.go
|
@ -222,11 +222,14 @@ func processUpload(upReq UploadRequest) (upload Upload, err error) {
|
||||||
return upload, FileTooLargeError
|
return upload, FileTooLargeError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the appropriate filename, then write to disk
|
// Determine the appropriate filename
|
||||||
barename, extension := barePlusExt(upReq.filename)
|
barename, extension := barePlusExt(upReq.filename)
|
||||||
|
randomize := false
|
||||||
|
|
||||||
|
// Randomize the "barename" (filename without extension) if needed
|
||||||
if upReq.randomBarename || len(barename) == 0 {
|
if upReq.randomBarename || len(barename) == 0 {
|
||||||
barename = generateBarename()
|
barename = generateBarename()
|
||||||
|
randomize = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var header []byte
|
var header []byte
|
||||||
|
@ -259,17 +262,33 @@ func processUpload(upReq UploadRequest) (upload Upload, err error) {
|
||||||
if merr == nil {
|
if merr == nil {
|
||||||
if upReq.deleteKey == metad.DeleteKey {
|
if upReq.deleteKey == metad.DeleteKey {
|
||||||
fileexists = false
|
fileexists = false
|
||||||
|
} else if Config.forceRandomFilename == true {
|
||||||
|
// the file exists
|
||||||
|
// the delete key doesn't match
|
||||||
|
// force random filenames is enabled
|
||||||
|
randomize = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if Config.forceRandomFilename == true {
|
||||||
|
// the file doesn't exist
|
||||||
|
// force random filenames is enabled
|
||||||
|
randomize = true
|
||||||
|
|
||||||
|
// set fileexists to true to generate a new barename
|
||||||
|
fileexists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for fileexists {
|
for fileexists {
|
||||||
|
if randomize {
|
||||||
|
barename = generateBarename()
|
||||||
|
} else {
|
||||||
counter, err := strconv.Atoi(string(barename[len(barename)-1]))
|
counter, err := strconv.Atoi(string(barename[len(barename)-1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
barename = barename + "1"
|
barename = barename + "1"
|
||||||
} else {
|
} else {
|
||||||
barename = barename[:len(barename)-1] + strconv.Itoa(counter+1)
|
barename = barename[:len(barename)-1] + strconv.Itoa(counter+1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
upload.Filename = strings.Join([]string{barename, extension}, ".")
|
upload.Filename = strings.Join([]string{barename, extension}, ".")
|
||||||
|
|
||||||
fileexists, err = storageBackend.Exists(upload.Filename)
|
fileexists, err = storageBackend.Exists(upload.Filename)
|
||||||
|
|
Loading…
Reference in New Issue