parent
a79bc1898a
commit
8f3108148b
9 changed files with 209 additions and 103 deletions
|
@ -52,6 +52,7 @@ allowhotlink = true
|
|||
- ```-xframeoptions "..." ``` -- X-Frame-Options header (default is "SAMEORIGIN")
|
||||
- ```-remoteuploads``` -- (optionally) enable remote uploads (/upload?url=https://...)
|
||||
- ```-nologs``` -- (optionally) disable request logs in stdout
|
||||
- ```-force-random-filename``` -- (optionally) force the use of random filenames
|
||||
|
||||
#### Storage backends
|
||||
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{
|
||||
"maxsize": Config.maxSize,
|
||||
"expirylist": listExpirationTimes(),
|
||||
"forcerandom": Config.forceRandomFilename,
|
||||
}, r, w)
|
||||
if err != nil {
|
||||
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) {
|
||||
err := renderTemplate(Templates["paste.html"], pongo2.Context{
|
||||
"expirylist": listExpirationTimes(),
|
||||
"forcerandom": Config.forceRandomFilename,
|
||||
}, r, w)
|
||||
if err != nil {
|
||||
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) {
|
||||
err := renderTemplate(Templates["API.html"], pongo2.Context{
|
||||
"siteurl": getSiteURL(r),
|
||||
"forcerandom": Config.forceRandomFilename,
|
||||
}, r, w)
|
||||
if err != nil {
|
||||
oopsHandler(c, w, r, RespHTML, "")
|
||||
|
|
|
@ -65,6 +65,7 @@ var Config struct {
|
|||
s3Region string
|
||||
s3Bucket string
|
||||
s3ForcePathStyle bool
|
||||
forceRandomFilename bool
|
||||
}
|
||||
|
||||
var Templates = make(map[string]*pongo2.Template)
|
||||
|
@ -268,6 +269,8 @@ func main() {
|
|||
"S3 bucket to use for files and metadata")
|
||||
flag.BoolVar(&Config.s3ForcePathStyle, "s3-force-path-style", false,
|
||||
"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()
|
||||
|
||||
|
|
|
@ -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) {
|
||||
mux := setup()
|
||||
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) {
|
||||
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 + '%)');
|
||||
},
|
||||
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);
|
||||
},
|
||||
success: function(file, resp) {
|
||||
|
|
|
@ -25,8 +25,10 @@
|
|||
|
||||
<p><strong>Optional headers with the request</strong></p>
|
||||
|
||||
{% if not forcerandom %}
|
||||
<p>Randomize the filename<br/>
|
||||
<code>Linx-Randomize: yes</code></p>
|
||||
{% endif %}
|
||||
|
||||
<p>Specify a custom deletion key<br/>
|
||||
<code>Linx-Delete-Key: mysecret</code></p>
|
||||
|
@ -56,30 +58,30 @@
|
|||
|
||||
{% if using_auth %}
|
||||
<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 %}
|
||||
<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 %}
|
||||
|
||||
<p>Uploading myphoto.jpg with an expiry of 20 minutes</p>
|
||||
|
||||
{% if using_auth %}
|
||||
<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 %}
|
||||
<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 %}
|
||||
|
||||
<p>Uploading myphoto.jpg with a random filename and getting a json response:</p>
|
||||
|
||||
{% 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",
|
||||
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
||||
{% 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",
|
||||
"sha256sum":"...","size":"...","url":"{{ siteurl }}f34h4iu.jpg"}</code></pre>
|
||||
{% endif %}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
|
||||
<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">
|
||||
<label>File expiry:
|
||||
<select name="expires" id="expires">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div id="main" class="paste">
|
||||
<div id="info">
|
||||
<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>
|
||||
<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
|
||||
}
|
||||
|
||||
// Determine the appropriate filename, then write to disk
|
||||
// Determine the appropriate filename
|
||||
barename, extension := barePlusExt(upReq.filename)
|
||||
randomize := false
|
||||
|
||||
// Randomize the "barename" (filename without extension) if needed
|
||||
if upReq.randomBarename || len(barename) == 0 {
|
||||
barename = generateBarename()
|
||||
randomize = true
|
||||
}
|
||||
|
||||
var header []byte
|
||||
|
@ -259,17 +262,33 @@ func processUpload(upReq UploadRequest) (upload Upload, err error) {
|
|||
if merr == nil {
|
||||
if upReq.deleteKey == metad.DeleteKey {
|
||||
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 {
|
||||
if randomize {
|
||||
barename = generateBarename()
|
||||
} else {
|
||||
counter, err := strconv.Atoi(string(barename[len(barename)-1]))
|
||||
if err != nil {
|
||||
barename = barename + "1"
|
||||
} else {
|
||||
barename = barename[:len(barename)-1] + strconv.Itoa(counter+1)
|
||||
}
|
||||
}
|
||||
upload.Filename = strings.Join([]string{barename, extension}, ".")
|
||||
|
||||
fileexists, err = storageBackend.Exists(upload.Filename)
|
||||
|
|
Loading…
Reference in a new issue