commit 7acde0980722f2fd82cb3713befb26fd4c7e4639
parent b36f3a06838f2e6353d524d7fe3e540a4341ef48
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Wed, 1 Feb 2023 20:43:44 -0800
direct file downloads
Diffstat:
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
}