Moving file response functionality to shared folder (#22226)
* Moving file response functionality to shared folder * Linter fixes * Added the attachment serve to shared folder * Removed the unwanted logs * import fixes * Linter fixes * License added * Moved UnsafeContentTypes and MediaContentTypes from api4 --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3ae70591d0
Коммит
825b281f36
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/audit"
|
"github.com/mattermost/mattermost-server/v6/audit"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) InitCloud() {
|
func (api *API) InitCloud() {
|
||||||
@@ -647,7 +648,7 @@ func getSubscriptionInvoicePDF(c *Context, w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileResponse(
|
web.WriteFileResponse(
|
||||||
filename,
|
filename,
|
||||||
"application/pdf",
|
"application/pdf",
|
||||||
int64(binary.Size(pdfData)),
|
int64(binary.Size(pdfData)),
|
||||||
|
|||||||
90
api4/file.go
90
api4/file.go
@@ -11,15 +11,14 @@ import (
|
|||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/app"
|
"github.com/mattermost/mattermost-server/v6/app"
|
||||||
"github.com/mattermost/mattermost-server/v6/audit"
|
"github.com/mattermost/mattermost-server/v6/audit"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/web"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
"github.com/mattermost/mattermost-server/v6/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,28 +29,6 @@ const (
|
|||||||
ThumbnailImageType = "image/jpeg"
|
ThumbnailImageType = "image/jpeg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var UnsafeContentTypes = [...]string{
|
|
||||||
"application/javascript",
|
|
||||||
"application/ecmascript",
|
|
||||||
"text/javascript",
|
|
||||||
"text/ecmascript",
|
|
||||||
"application/x-javascript",
|
|
||||||
"text/html",
|
|
||||||
}
|
|
||||||
|
|
||||||
var MediaContentTypes = [...]string{
|
|
||||||
"image/jpeg",
|
|
||||||
"image/png",
|
|
||||||
"image/bmp",
|
|
||||||
"image/gif",
|
|
||||||
"image/tiff",
|
|
||||||
"video/avi",
|
|
||||||
"video/mpeg",
|
|
||||||
"video/mp4",
|
|
||||||
"audio/mpeg",
|
|
||||||
"audio/wav",
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxMultipartFormDataBytes = 10 * 1024 // 10Kb
|
const maxMultipartFormDataBytes = 10 * 1024 // 10Kb
|
||||||
|
|
||||||
func (api *API) InitFile() {
|
func (api *API) InitFile() {
|
||||||
@@ -502,7 +479,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
auditRec.Success()
|
auditRec.Success()
|
||||||
|
|
||||||
writeFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
web.WriteFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -537,7 +514,7 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
|
|
||||||
writeFileResponse(info.Name, ThumbnailImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
web.WriteFileResponse(info.Name, ThumbnailImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileLink(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getFileLink(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -614,7 +591,7 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
|
|
||||||
writeFileResponse(info.Name, PreviewImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
web.WriteFileResponse(info.Name, PreviewImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -681,64 +658,7 @@ func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
|
|
||||||
writeFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, false, w, r)
|
web.WriteFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, false, w, r)
|
||||||
}
|
|
||||||
|
|
||||||
func writeFileResponse(filename string, contentType string, contentSize int64, lastModification time.Time, webserverMode string, fileReader io.ReadSeeker, forceDownload bool, w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Cache-Control", "private, no-cache")
|
|
||||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
||||||
|
|
||||||
if contentSize > 0 {
|
|
||||||
contentSizeStr := strconv.Itoa(int(contentSize))
|
|
||||||
if webserverMode == "gzip" {
|
|
||||||
w.Header().Set("X-Uncompressed-Content-Length", contentSizeStr)
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Length", contentSizeStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if contentType == "" {
|
|
||||||
contentType = "application/octet-stream"
|
|
||||||
} else {
|
|
||||||
for _, unsafeContentType := range UnsafeContentTypes {
|
|
||||||
if strings.HasPrefix(contentType, unsafeContentType) {
|
|
||||||
contentType = "text/plain"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", contentType)
|
|
||||||
|
|
||||||
var toDownload bool
|
|
||||||
if forceDownload {
|
|
||||||
toDownload = true
|
|
||||||
} else {
|
|
||||||
isMediaType := false
|
|
||||||
|
|
||||||
for _, mediaContentType := range MediaContentTypes {
|
|
||||||
if strings.HasPrefix(contentType, mediaContentType) {
|
|
||||||
isMediaType = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toDownload = !isMediaType
|
|
||||||
}
|
|
||||||
|
|
||||||
filename = url.PathEscape(filename)
|
|
||||||
|
|
||||||
if toDownload {
|
|
||||||
w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Disposition", "inline;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent file links from being embedded in iframes
|
|
||||||
w.Header().Set("X-Frame-Options", "DENY")
|
|
||||||
w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'")
|
|
||||||
|
|
||||||
http.ServeContent(w, r, filename, lastModification, fileReader)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchFilesInTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
func searchFilesInTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/web"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
"github.com/mattermost/mattermost-server/v6/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -275,7 +276,7 @@ func selfHostedInvoicePDF(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileResponse(
|
web.WriteFileResponse(
|
||||||
filename,
|
filename,
|
||||||
"application/pdf",
|
"application/pdf",
|
||||||
int64(binary.Size(pdfData)),
|
int64(binary.Size(pdfData)),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/audit"
|
"github.com/mattermost/mattermost-server/v6/audit"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) InitJob() {
|
func (api *API) InitJob() {
|
||||||
@@ -100,7 +101,7 @@ func downloadJob(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go)
|
// We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go)
|
||||||
// already sets that for us
|
// already sets that for us
|
||||||
writeFileResponse(fileName, FileMime, 0, time.Unix(0, job.LastActivityAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, true, w, r)
|
web.WriteFileResponse(fileName, FileMime, 0, time.Unix(0, job.LastActivityAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, true, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createJob(c *Context, w http.ResponseWriter, r *http.Request) {
|
func createJob(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/services/cache"
|
"github.com/mattermost/mattermost-server/v6/services/cache"
|
||||||
"github.com/mattermost/mattermost-server/v6/services/upgrader"
|
"github.com/mattermost/mattermost-server/v6/services/upgrader"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -122,7 +123,7 @@ func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
// Send the zip file back to client
|
// Send the zip file back to client
|
||||||
// We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go)
|
// We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go)
|
||||||
// already sets that for us
|
// already sets that for us
|
||||||
writeFileResponse(outputZipFilename, FileMime, 0, now, *c.App.Config().ServiceSettings.WebserverMode, fileBytesReader, true, w, r)
|
web.WriteFileResponse(outputZipFilename, FileMime, 0, now, *c.App.Config().ServiceSettings.WebserverMode, fileBytesReader, true, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
92
shared/web/files.go
Обычный файл
92
shared/web/files.go
Обычный файл
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var UnsafeContentTypes = [...]string{
|
||||||
|
"application/javascript",
|
||||||
|
"application/ecmascript",
|
||||||
|
"text/javascript",
|
||||||
|
"text/ecmascript",
|
||||||
|
"application/x-javascript",
|
||||||
|
"text/html",
|
||||||
|
}
|
||||||
|
|
||||||
|
var MediaContentTypes = [...]string{
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/bmp",
|
||||||
|
"image/gif",
|
||||||
|
"image/tiff",
|
||||||
|
"video/avi",
|
||||||
|
"video/mpeg",
|
||||||
|
"video/mp4",
|
||||||
|
"audio/mpeg",
|
||||||
|
"audio/wav",
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteFileResponse(filename string, contentType string, contentSize int64, lastModification time.Time, webserverMode string, fileReader io.ReadSeeker, forceDownload bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "private, no-cache")
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
|
||||||
|
if contentSize > 0 {
|
||||||
|
contentSizeStr := strconv.Itoa(int(contentSize))
|
||||||
|
if webserverMode == "gzip" {
|
||||||
|
w.Header().Set("X-Uncompressed-Content-Length", contentSizeStr)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Length", contentSizeStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "application/octet-stream"
|
||||||
|
} else {
|
||||||
|
for _, unsafeContentType := range UnsafeContentTypes {
|
||||||
|
if strings.HasPrefix(contentType, unsafeContentType) {
|
||||||
|
contentType = "text/plain"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
var toDownload bool
|
||||||
|
if forceDownload {
|
||||||
|
toDownload = true
|
||||||
|
} else {
|
||||||
|
isMediaType := false
|
||||||
|
|
||||||
|
for _, mediaContentType := range MediaContentTypes {
|
||||||
|
if strings.HasPrefix(contentType, mediaContentType) {
|
||||||
|
isMediaType = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toDownload = !isMediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = url.PathEscape(filename)
|
||||||
|
|
||||||
|
if toDownload {
|
||||||
|
w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Disposition", "inline;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent file links from being embedded in iframes
|
||||||
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'")
|
||||||
|
|
||||||
|
http.ServeContent(w, r, filename, lastModification, fileReader)
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user