commit d3de3db0777c66c3f7cdff88a9138d33e907f616
parent 62e02c9e36c34384fcb656b63208541a28c36622
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Mon, 12 Jun 2023 20:52:20 -0700
move file
Diffstat:
2 files changed, 175 insertions(+), 162 deletions(-)
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -3,19 +3,31 @@ package handlers
import (
"bytes"
"context"
+ "dkforest/pkg/cache"
+ "dkforest/pkg/captcha"
+ "dkforest/pkg/config"
+ "dkforest/pkg/database"
dutils "dkforest/pkg/database/utils"
+ "dkforest/pkg/managers"
"dkforest/pkg/odometer"
"dkforest/pkg/pubsub"
+ "dkforest/pkg/utils"
v1 "dkforest/pkg/web/handlers/api/v1"
"dkforest/pkg/web/handlers/streamModals"
+ hutils "dkforest/pkg/web/handlers/utils"
"encoding/base64"
"fmt"
+ humanize "github.com/dustin/go-humanize"
+ "github.com/labstack/echo"
+ "github.com/pquerna/otp"
+ "github.com/pquerna/otp/totp"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/bcrypt"
_ "golang.org/x/image/bmp"
_ "golang.org/x/image/webp"
"image"
_ "image/gif"
"image/png"
- "io"
"io/ioutil"
"net/http"
"net/url"
@@ -26,21 +38,6 @@ import (
"strings"
"syscall"
"time"
- "unicode/utf8"
-
- "dkforest/pkg/cache"
- "dkforest/pkg/captcha"
- "dkforest/pkg/config"
- "dkforest/pkg/database"
- "dkforest/pkg/managers"
- "dkforest/pkg/utils"
- hutils "dkforest/pkg/web/handlers/utils"
- humanize "github.com/dustin/go-humanize"
- "github.com/labstack/echo"
- "github.com/pquerna/otp"
- "github.com/pquerna/otp/totp"
- "github.com/sirupsen/logrus"
- "golang.org/x/crypto/bcrypt"
)
var tempLoginCache = cache.New[TempLoginCaptcha](3*time.Minute, 3*time.Minute)
@@ -726,152 +723,6 @@ func PublicUserProfilePGPHandler(c echo.Context) error {
return c.String(http.StatusOK, user.GPGPublicKey)
}
-func isImageMimeType(mimeType string) bool {
- return mimeType == "image/jpeg" ||
- mimeType == "image/png" ||
- mimeType == "image/gif" ||
- mimeType == "image/bmp" ||
- mimeType == "image/x-icon" ||
- mimeType == "image/webp"
-}
-
-func isAttachmentMimeType(mimeType string) bool {
- return mimeType == "application/x-gzip" ||
- mimeType == "application/zip" ||
- mimeType == "application/x-rar-compressed" ||
- mimeType == "application/pdf" ||
- mimeType == "audio/basic" ||
- mimeType == "audio/aiff" ||
- mimeType == "audio/mpeg" ||
- mimeType == "application/ogg" ||
- mimeType == "audio/midi" ||
- mimeType == "video/avi" ||
- mimeType == "audio/wave" ||
- mimeType == "video/webm" ||
- mimeType == "font/ttf" ||
- mimeType == "font/otf" ||
- mimeType == "font/collection" ||
- mimeType == "font/woff" ||
- mimeType == "font/woff2" ||
- mimeType == "application/wasm" ||
- mimeType == "application/postscript" ||
- mimeType == "application/vnd.ms-fontobject" ||
- mimeType == "application/octet-stream"
-}
-
-func UploadsDownloadHandler(c echo.Context) error {
- authUser := c.Get("authUser").(*database.User)
- db := c.Get("database").(*database.DkfDB)
- filename := c.Param("filename")
- file, err := db.GetUploadByFileName(filename)
- if err != nil {
- return c.Render(http.StatusOK, "standalone.upload404", nil)
- }
- if !file.Exists() {
- logrus.Error(filename + " does not exists")
- return c.Render(http.StatusOK, "standalone.upload404", nil)
- }
-
- if file.FileSize < config.MaxFileSizeBeforeDownload {
- fi, decFileBytes, err := file.GetContent()
- if err != nil || fi.IsDir() {
- return c.Render(http.StatusOK, "standalone.upload404", nil)
- }
- buf := bytes.NewReader(decFileBytes)
-
- // Validate image type and determine extension
- mimeType, err := GetFileContentType(buf)
- _, err = buf.Seek(0, io.SeekStart)
- if err != nil {
- return c.Render(http.StatusOK, "standalone.upload404", nil)
- }
-
- // Serve images
- if isImageMimeType(mimeType) {
- http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
- return nil
- }
-
- if mimeType == "application/octet-stream" && utf8.Valid(decFileBytes) {
- mimeType = "text/plain; charset=utf-8"
- }
-
- // MimeType that always trigger a file "download"
- if isAttachmentMimeType(mimeType) {
- // Keep track of user downloads
- if _, err := db.CreateDownload(authUser.ID, filename); err != nil {
- logrus.Error(err)
- }
- c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", "attachment", file.OrigFileName))
- http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
- return nil
- }
-
- // Serve any other file as text/plain
- c.Response().Header().Set(echo.HeaderContentType, "text/plain")
- c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("filename=%q", file.OrigFileName))
- http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
- return nil
- }
-
- userNbDownloaded := db.UserNbDownloaded(authUser.ID, filename)
-
- // Display captcha to new users, or old users if they already downloaded the file.
- if !authUser.AccountOldEnough() || userNbDownloaded >= 1 {
- // Captcha for bigger files
- var data captchaRequiredData
- data.CaptchaDescription = "Captcha required"
- if !authUser.AccountOldEnough() {
- data.CaptchaDescription = fmt.Sprintf("Account that are less than 3 days old must complete the captcha to download files bigger than %s", humanize.Bytes(config.MaxFileSizeBeforeDownload))
- } else if userNbDownloaded >= 1 {
- data.CaptchaDescription = fmt.Sprintf("For the second download onward of a file bigger than %s, you must complete the captcha", humanize.Bytes(config.MaxFileSizeBeforeDownload))
- }
- data.CaptchaID, data.CaptchaImg = captcha.New()
- const captchaRequiredTmpl = "captcha-required"
- if c.Request().Method == http.MethodGet {
- return c.Render(http.StatusOK, captchaRequiredTmpl, data)
- }
- captchaID := c.Request().PostFormValue("captcha_id")
- captchaInput := c.Request().PostFormValue("captcha")
- if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
- data.ErrCaptcha = err.Error()
- return c.Render(http.StatusOK, captchaRequiredTmpl, data)
- }
- }
-
- // Keep track of user downloads
- if _, err := db.CreateDownload(authUser.ID, filename); err != nil {
- logrus.Error(err)
- }
-
- fi, decFileBytes, err := file.GetContent()
- if err != nil {
- return c.Render(http.StatusOK, "standalone.upload404", nil)
- }
- buf := bytes.NewReader(decFileBytes)
-
- c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", "attachment", file.OrigFileName))
- http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
- return nil
-}
-
-func GetFileContentType(out io.ReadSeeker) (string, error) {
-
- // Only the first 512 bytes are used to sniff the content type.
- buffer := make([]byte, 512)
-
- _, err := out.Read(buffer)
- if err != nil {
- return "", err
- }
-
- // Use the net/http package's handy DectectContentType function. Always returns a valid
- // content-type by returning "application/octet-stream" if no others seemed to match.
- contentType := http.DetectContentType(buffer)
-
- return contentType, nil
-}
-
func BHCHandler(c echo.Context) error {
/*
We have a script that check BHC wait room and kick any users that has not completed the dkf captcha.
diff --git a/pkg/web/handlers/uploads.go b/pkg/web/handlers/uploads.go
@@ -0,0 +1,162 @@
+package handlers
+
+import (
+ "bytes"
+ "dkforest/pkg/captcha"
+ "dkforest/pkg/config"
+ "dkforest/pkg/database"
+ hutils "dkforest/pkg/web/handlers/utils"
+ "fmt"
+ "github.com/dustin/go-humanize"
+ "github.com/labstack/echo"
+ "github.com/sirupsen/logrus"
+ "io"
+ "net/http"
+ "unicode/utf8"
+)
+
+func isImageMimeType(mimeType string) bool {
+ return mimeType == "image/jpeg" ||
+ mimeType == "image/png" ||
+ mimeType == "image/gif" ||
+ mimeType == "image/bmp" ||
+ mimeType == "image/x-icon" ||
+ mimeType == "image/webp"
+}
+
+func isAttachmentMimeType(mimeType string) bool {
+ return mimeType == "application/x-gzip" ||
+ mimeType == "application/zip" ||
+ mimeType == "application/x-rar-compressed" ||
+ mimeType == "application/pdf" ||
+ mimeType == "audio/basic" ||
+ mimeType == "audio/aiff" ||
+ mimeType == "audio/mpeg" ||
+ mimeType == "application/ogg" ||
+ mimeType == "audio/midi" ||
+ mimeType == "video/avi" ||
+ mimeType == "audio/wave" ||
+ mimeType == "video/webm" ||
+ mimeType == "font/ttf" ||
+ mimeType == "font/otf" ||
+ mimeType == "font/collection" ||
+ mimeType == "font/woff" ||
+ mimeType == "font/woff2" ||
+ mimeType == "application/wasm" ||
+ mimeType == "application/postscript" ||
+ mimeType == "application/vnd.ms-fontobject" ||
+ mimeType == "application/octet-stream"
+}
+
+func UploadsDownloadHandler(c echo.Context) error {
+ authUser := c.Get("authUser").(*database.User)
+ db := c.Get("database").(*database.DkfDB)
+ filename := c.Param("filename")
+ file, err := db.GetUploadByFileName(filename)
+ if err != nil {
+ return c.Render(http.StatusOK, "standalone.upload404", nil)
+ }
+ if !file.Exists() {
+ logrus.Error(filename + " does not exists")
+ return c.Render(http.StatusOK, "standalone.upload404", nil)
+ }
+
+ if file.FileSize < config.MaxFileSizeBeforeDownload {
+ fi, decFileBytes, err := file.GetContent()
+ if err != nil || fi.IsDir() {
+ return c.Render(http.StatusOK, "standalone.upload404", nil)
+ }
+ buf := bytes.NewReader(decFileBytes)
+
+ // Validate image type and determine extension
+ mimeType, err := getFileContentType(buf)
+ _, err = buf.Seek(0, io.SeekStart)
+ if err != nil {
+ return c.Render(http.StatusOK, "standalone.upload404", nil)
+ }
+
+ // Serve images
+ if isImageMimeType(mimeType) {
+ http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
+ return nil
+ }
+
+ if mimeType == "application/octet-stream" && utf8.Valid(decFileBytes) {
+ mimeType = "text/plain; charset=utf-8"
+ }
+
+ // MimeType that always trigger a file "download"
+ if isAttachmentMimeType(mimeType) {
+ // Keep track of user downloads
+ if _, err := db.CreateDownload(authUser.ID, filename); err != nil {
+ logrus.Error(err)
+ }
+ c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", "attachment", file.OrigFileName))
+ http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
+ return nil
+ }
+
+ // Serve any other file as text/plain
+ c.Response().Header().Set(echo.HeaderContentType, "text/plain")
+ c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("filename=%q", file.OrigFileName))
+ http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
+ return nil
+ }
+
+ userNbDownloaded := db.UserNbDownloaded(authUser.ID, filename)
+
+ // Display captcha to new users, or old users if they already downloaded the file.
+ if !authUser.AccountOldEnough() || userNbDownloaded >= 1 {
+ // Captcha for bigger files
+ var data captchaRequiredData
+ data.CaptchaDescription = "Captcha required"
+ if !authUser.AccountOldEnough() {
+ data.CaptchaDescription = fmt.Sprintf("Account that are less than 3 days old must complete the captcha to download files bigger than %s", humanize.Bytes(config.MaxFileSizeBeforeDownload))
+ } else if userNbDownloaded >= 1 {
+ data.CaptchaDescription = fmt.Sprintf("For the second download onward of a file bigger than %s, you must complete the captcha", humanize.Bytes(config.MaxFileSizeBeforeDownload))
+ }
+ data.CaptchaID, data.CaptchaImg = captcha.New()
+ const captchaRequiredTmpl = "captcha-required"
+ if c.Request().Method == http.MethodGet {
+ return c.Render(http.StatusOK, captchaRequiredTmpl, data)
+ }
+ captchaID := c.Request().PostFormValue("captcha_id")
+ captchaInput := c.Request().PostFormValue("captcha")
+ if err := hutils.CaptchaVerifyString(c, captchaID, captchaInput); err != nil {
+ data.ErrCaptcha = err.Error()
+ return c.Render(http.StatusOK, captchaRequiredTmpl, data)
+ }
+ }
+
+ // Keep track of user downloads
+ if _, err := db.CreateDownload(authUser.ID, filename); err != nil {
+ logrus.Error(err)
+ }
+
+ fi, decFileBytes, err := file.GetContent()
+ if err != nil {
+ return c.Render(http.StatusOK, "standalone.upload404", nil)
+ }
+ buf := bytes.NewReader(decFileBytes)
+
+ c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", "attachment", file.OrigFileName))
+ http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), buf)
+ return nil
+}
+
+func getFileContentType(out io.ReadSeeker) (string, error) {
+
+ // Only the first 512 bytes are used to sniff the content type.
+ buffer := make([]byte, 512)
+
+ _, err := out.Read(buffer)
+ if err != nil {
+ return "", err
+ }
+
+ // Use the net/http package's handy DectectContentType function. Always returns a valid
+ // content-type by returning "application/octet-stream" if no others seemed to match.
+ contentType := http.DetectContentType(buffer)
+
+ return contentType, nil
+}