dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit 7acde0980722f2fd82cb3713befb26fd4c7e4639
parent b36f3a06838f2e6353d524d7fe3e540a4341ef48
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Wed,  1 Feb 2023 20:43:44 -0800

direct file downloads

Diffstat:
Mpkg/actions/actions.go | 6++++++
Mpkg/config/config.go | 37++++++++++++++++++++++++++-----------
Mpkg/web/handlers/handlers.go | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/web/web.go | 1+
4 files changed, 87 insertions(+), 11 deletions(-)

diff --git a/pkg/actions/actions.go b/pkg/actions/actions.go @@ -165,6 +165,12 @@ func ensureProjectHome() { if err := os.MkdirAll(config.Global.ProjectFiledropPath(), 0755); err != nil { logrus.Fatal("Failed to create dkforest filedrop folder", err) } + + // Contains files that we offer for download directly + config.Global.SetProjectDownloadsPath(filepath.Join(projectPath, "downloads")) + if err := os.MkdirAll(config.Global.ProjectDownloadsPath(), 0755); err != nil { + logrus.Fatal("Failed to create dkforest downloads folder", err) + } } // LogFormatter ... diff --git a/pkg/config/config.go b/pkg/config/config.go @@ -153,17 +153,18 @@ var ConnMap = NewConnManager() // GlobalConf ... type GlobalConf struct { sync.RWMutex - appVersion *version.Version - projectPath string // project path - projectLocalsPath string // directory where we keep custom translation files - projectHtmlPath string - projectUploadsPath string - projectFiledropPath string - sha string - masterKey string - cookieSecure bool - cookieDomain string - baseURL string // (http://127.0.0.1:8080) + appVersion *version.Version + projectPath string // project path + projectLocalsPath string // directory where we keep custom translation files + projectHtmlPath string + projectUploadsPath string + projectFiledropPath string + projectDownloadsPath string + sha string + masterKey string + cookieSecure bool + cookieDomain string + baseURL string // (http://127.0.0.1:8080) } // NewGlobalConf ... @@ -313,6 +314,20 @@ func (c *GlobalConf) SetProjectFiledropPath(projectFiledropPath string) { c.projectFiledropPath = projectFiledropPath } +// ProjectDownloadsPath ... +func (c *GlobalConf) ProjectDownloadsPath() string { + c.RLock() + defer c.RUnlock() + return c.projectDownloadsPath +} + +// SetProjectDownloadsPath ... +func (c *GlobalConf) SetProjectDownloadsPath(projectDownloadsPath string) { + c.Lock() + defer c.Unlock() + c.projectDownloadsPath = projectDownloadsPath +} + // BaseURL ... func (c *GlobalConf) BaseURL() string { c.RLock() diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go @@ -4448,6 +4448,60 @@ func FileDropTmpHandler(c echo.Context) error { return c.NoContent(http.StatusOK) } +func FileDropDownloadHandler(c echo.Context) error { + authUser, ok := c.Get("authUser").(*database.User) + if !ok { + return c.Redirect(http.StatusFound, "/") + } + + fileName := c.Param("fileName") + if !utils.FileExists(filepath.Join(config.Global.ProjectDownloadsPath(), fileName)) { + logrus.Error(fileName + " does not exists") + return c.Redirect(http.StatusFound, "/") + } + + // Display captcha to new users, or old users if they already downloaded the file. + if !authUser.AccountOldEnough() || database.UserNbDownloaded(authUser.ID, fileName) >= 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 database.UserNbDownloaded(authUser.ID, fileName) >= 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 := database.CreateDownload(authUser.ID, fileName); err != nil { + logrus.Error(err) + } + + f, err := os.Open(filepath.Join(config.Global.ProjectDownloadsPath(), fileName)) + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + fi, err := f.Stat() + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + + c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", "attachment", fileName)) + http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f) + return nil +} + func Stego1ChallengeHandler(c echo.Context) error { const flagHash = "05b456689a9f8de69416d21cbb97157588b8491d07551167a95b93a1c7d61e7b" authUser := c.Get("authUser").(*database.User) diff --git a/pkg/web/web.go b/pkg/web/web.go @@ -303,6 +303,7 @@ func getBaseServer() *echo.Echo { e.POST("/file-drop/:uuid", handlers.FileDropHandler) e.POST("/file-drop/:uuid/tmp-init", handlers.FileDropTmpInitHandler) e.POST("/file-drop/:uuid/tmp", handlers.FileDropTmpHandler) + e.GET("/downloads/:fileName", handlers.FileDropDownloadHandler) e.Any("*", getMainServer(i18nBundle, renderer)) return e }