|
|
|
|
@ -1,12 +1,17 @@
|
|
|
|
|
package middleware
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/application/constants"
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
|
|
|
|
filemanagerfs "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/sharepreview"
|
|
|
|
|
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// FrontendFileHandler 前端静态文件处理
|
|
|
|
|
@ -50,8 +55,12 @@ func FrontendFileHandler(dep dependency.Dep) gin.HandlerFunc {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if renderSharePreviewForBot(c) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 不存在的路径和index.html均返回index.html
|
|
|
|
|
if (path == "/index.html") || (path == "/") || !fs.Exists("/", path) {
|
|
|
|
|
if path == "/index.html" || path == "/" || !fs.Exists("/", path) {
|
|
|
|
|
// 读取、替换站点设置
|
|
|
|
|
settingClient := dep.SettingProvider()
|
|
|
|
|
siteBasic := settingClient.SiteBasic(c)
|
|
|
|
|
@ -84,3 +93,95 @@ func FrontendFileHandler(dep dependency.Dep) gin.HandlerFunc {
|
|
|
|
|
c.Abort()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderSharePreviewForBot(c *gin.Context) bool {
|
|
|
|
|
path := c.Request.URL.Path
|
|
|
|
|
if path != "/home" && path != "/home/" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if !util.IsSocialMediaBot(c.GetHeader("User-Agent")) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
shareURI := parseShareURI(c.Query("path"))
|
|
|
|
|
if shareURI == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
shareID := shareURI.ID("")
|
|
|
|
|
if shareID == "" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html, err := sharepreview.RenderOGPage(c, sharepreview.Options{
|
|
|
|
|
ID: shareID,
|
|
|
|
|
Password: shareURI.Password(),
|
|
|
|
|
SharePath: shareURI.PathTrimmed(),
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
|
|
|
c.String(http.StatusOK, html)
|
|
|
|
|
c.Abort()
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseShareURI(raw string) *filemanagerfs.URI {
|
|
|
|
|
if raw == "" {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
shareURI, err := filemanagerfs.NewUriFromString(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
sanitized := sanitizeInvalidPercentEscapes(raw)
|
|
|
|
|
if sanitized != raw {
|
|
|
|
|
shareURI, err = filemanagerfs.NewUriFromString(sanitized)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if err != nil && strings.Contains(raw, "%") {
|
|
|
|
|
unescaped, err := url.QueryUnescape(raw)
|
|
|
|
|
if err == nil {
|
|
|
|
|
shareURI, err = filemanagerfs.NewUriFromString(unescaped)
|
|
|
|
|
if err != nil {
|
|
|
|
|
sanitized := sanitizeInvalidPercentEscapes(unescaped)
|
|
|
|
|
if sanitized != unescaped {
|
|
|
|
|
shareURI, err = filemanagerfs.NewUriFromString(sanitized)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if shareURI.FileSystem() != constants.FileSystemShare {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return shareURI
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sanitizeInvalidPercentEscapes(raw string) string {
|
|
|
|
|
if !strings.Contains(raw, "%") {
|
|
|
|
|
return raw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
b.Grow(len(raw))
|
|
|
|
|
for i := 0; i < len(raw); i++ {
|
|
|
|
|
if raw[i] != '%' {
|
|
|
|
|
b.WriteByte(raw[i])
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if i+2 < len(raw) && isHexDigit(raw[i+1]) && isHexDigit(raw[i+2]) {
|
|
|
|
|
b.WriteByte('%')
|
|
|
|
|
b.WriteByte(raw[i+1])
|
|
|
|
|
b.WriteByte(raw[i+2])
|
|
|
|
|
i += 2
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Replace stray % to avoid url.Parse failures.
|
|
|
|
|
b.WriteString("%25")
|
|
|
|
|
}
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isHexDigit(b byte) bool {
|
|
|
|
|
return ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
|
|
|
|
|
}
|
|
|
|
|
|