Skip to content

Commit

Permalink
Merge pull request #117 from DanielHeath/implement-file-uploads
Browse files Browse the repository at this point in the history
Implement file upload / download, fix #77
  • Loading branch information
schollz authored Feb 20, 2018
2 parents 4ebf9e0 + efd4cfb commit 50624e7
Show file tree
Hide file tree
Showing 8 changed files with 4,094 additions and 14 deletions.
56 changes: 51 additions & 5 deletions bindata.go

Large diffs are not rendered by default.

76 changes: 73 additions & 3 deletions handlers.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package main

import (
"crypto/sha256"
"fmt"
"html/template"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strconv"
"strings"
Expand All @@ -22,6 +26,8 @@ var customCSS []byte
var defaultLock string
var debounceTime int
var diaryMode bool
var allowFileUploads bool
var maxUploadMB uint

func serve(
host,
Expand All @@ -39,6 +45,7 @@ func serve(
allowInsecure bool,
hotTemplateReloading bool,
) {

if hotTemplateReloading {
gin.SetMode(gin.DebugMode)
} else {
Expand All @@ -63,8 +70,8 @@ func serve(
page := c.Param("page")
cmd := c.Param("command")

if page == "sitemap.xml" || page == "favicon.ico" || page == "static" {
return false // no auth for sitemap
if page == "sitemap.xml" || page == "favicon.ico" || page == "static" || page == "uploads" {
return false // no auth for these
}

if page != "" && cmd == "/read" {
Expand All @@ -88,6 +95,9 @@ func serve(
c.Redirect(302, "/"+randomAlliterateCombo())
}
})

router.POST("/uploads", handleUpload)

router.GET("/:page", func(c *gin.Context) {
page := c.Param("page")
c.Redirect(302, "/"+page+"/")
Expand Down Expand Up @@ -135,7 +145,7 @@ func serve(
if TLS {
http.ListenAndServeTLS(host+":"+port, crt_path, key_path, router)
} else {
router.Run(host + ":" + port)
panic(router.Run(host + ":" + port))
}
}

Expand Down Expand Up @@ -267,7 +277,26 @@ func handlePageRequest(c *gin.Context) {
}
c.Data(http.StatusOK, contentType(filename), data)
return
} else if page == "uploads" {
pathname := path.Join(pathToData, command[1:]+".upload")

if allowInsecureHtml {
c.Header(
"Content-Disposition",
`inline; filename="`+c.DefaultQuery("filename", "upload")+`"`,
)
} else {
// Prevent malicious html uploads by forcing type to plaintext and 'download-instead-of-view'
c.Header("Content-Type", "text/plain")
c.Header(
"Content-Disposition",
`attachment; filename="`+c.DefaultQuery("filename", "upload")+`"`,
)
}
c.File(pathname)
return
}

p := Open(page)
if len(command) < 2 {
if p.IsPublished {
Expand Down Expand Up @@ -394,6 +423,8 @@ func handlePageRequest(c *gin.Context) {
"Date": time.Now().Format("2006-01-02"),
"UnixTime": time.Now().Unix(),
"ChildPageNames": p.ChildPageNames(),
"AllowFileUploads": allowFileUploads,
"MaxUploadMB": maxUploadMB,
})
}

Expand Down Expand Up @@ -583,6 +614,45 @@ func handlePublish(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}

func handleUpload(c *gin.Context) {
if !allowFileUploads {
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Uploads are disabled on this server"))
return
}

file, info, err := c.Request.FormFile("file")
defer file.Close()
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}

h := sha256.New()
if _, err := io.Copy(h, file); err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}

newName := "sha256-" + encodeBytesToBase32(h.Sum(nil))

// Replaces any existing version, but sha256 collisions are rare as anything.
outfile, err := os.Create(path.Join(pathToData, newName+".upload"))
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}

file.Seek(0, io.SeekStart)
_, err = io.Copy(outfile, file)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}

c.Header("Location", "/uploads/"+newName+"?filename="+url.QueryEscape(info.Filename))
return
}

func handleEncrypt(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
Expand Down
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func main() {
fmt.Printf("\nRunning cowyo server (version %s) at http://%s:%s\n\n", version, host, c.GlobalString("port"))
}

allowFileUploads = c.GlobalBool("allow-file-uploads")
maxUploadMB = c.GlobalUint("max-upload-mb")

serve(
c.GlobalString("host"),
c.GlobalString("port"),
Expand Down Expand Up @@ -131,6 +134,15 @@ func main() {
Value: "secret",
Usage: "random data to use for cookies; changing it will invalidate all sessions",
},
cli.BoolFlag{
Name: "allow-file-uploads",
Usage: "Enable file uploads",
},
cli.UintFlag{
Name: "max-upload-mb",
Value: 2,
Usage: "Largest file upload (in mb) allowed",
},
}
app.Commands = []cli.Command{
{
Expand Down
Loading

0 comments on commit 50624e7

Please sign in to comment.