mirror of
https://github.com/AlistGo/alist.git
synced 2025-11-25 19:37:41 +08:00
feat(cloud189): Added sanitization for file and folder names (#9366)
- Introduced `sanitizeName` function to remove four-byte characters (e.g., emojis) from names before upload or creation. - Added `StripEmoji` option in driver configurations for cloud189 and cloud189pc. - Updated file and folder operations (upload, rename, and creation) to use sanitized names. - Ensured compatibility with both cloud189 and cloud189pc implementations.
This commit is contained in:
@@ -80,9 +80,10 @@ func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Cloud189) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *Cloud189) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
safeName := d.sanitizeName(dirName)
|
||||||
form := map[string]string{
|
form := map[string]string{
|
||||||
"parentFolderId": parentDir.GetID(),
|
"parentFolderId": parentDir.GetID(),
|
||||||
"folderName": dirName,
|
"folderName": safeName,
|
||||||
}
|
}
|
||||||
_, err := d.request("https://cloud.189.cn/api/open/file/createFolder.action", http.MethodPost, func(req *resty.Request) {
|
_, err := d.request("https://cloud.189.cn/api/open/file/createFolder.action", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetFormData(form)
|
req.SetFormData(form)
|
||||||
@@ -126,9 +127,10 @@ func (d *Cloud189) Rename(ctx context.Context, srcObj model.Obj, newName string)
|
|||||||
idKey = "folderId"
|
idKey = "folderId"
|
||||||
nameKey = "destFolderName"
|
nameKey = "destFolderName"
|
||||||
}
|
}
|
||||||
|
safeName := d.sanitizeName(newName)
|
||||||
form := map[string]string{
|
form := map[string]string{
|
||||||
idKey: srcObj.GetID(),
|
idKey: srcObj.GetID(),
|
||||||
nameKey: newName,
|
nameKey: safeName,
|
||||||
}
|
}
|
||||||
_, err := d.request(url, http.MethodPost, func(req *resty.Request) {
|
_, err := d.request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetFormData(form)
|
req.SetFormData(form)
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
Username string `json:"username" required:"true"`
|
Username string `json:"username" required:"true"`
|
||||||
Password string `json:"password" required:"true"`
|
Password string `json:"password" required:"true"`
|
||||||
Cookie string `json:"cookie" help:"Fill in the cookie if need captcha"`
|
Cookie string `json:"cookie" help:"Fill in the cookie if need captcha"`
|
||||||
|
StripEmoji bool `json:"strip_emoji" help:"Remove four-byte characters (e.g., emoji) before upload"`
|
||||||
driver.RootID
|
driver.RootID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
@@ -222,13 +224,37 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Cloud189) sanitizeName(name string) string {
|
||||||
|
if !d.StripEmoji {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
b := strings.Builder{}
|
||||||
|
for _, r := range name {
|
||||||
|
if utf8.RuneLen(r) == 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
sanitized := b.String()
|
||||||
|
if sanitized == "" {
|
||||||
|
ext := path.Ext(name)
|
||||||
|
if ext != "" {
|
||||||
|
sanitized = "file" + ext
|
||||||
|
} else {
|
||||||
|
sanitized = "file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitized
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Cloud189) oldUpload(dstDir model.Obj, file model.FileStreamer) error {
|
func (d *Cloud189) oldUpload(dstDir model.Obj, file model.FileStreamer) error {
|
||||||
|
safeName := d.sanitizeName(file.GetName())
|
||||||
res, err := d.client.R().SetMultipartFormData(map[string]string{
|
res, err := d.client.R().SetMultipartFormData(map[string]string{
|
||||||
"parentId": dstDir.GetID(),
|
"parentId": dstDir.GetID(),
|
||||||
"sessionKey": "??",
|
"sessionKey": "??",
|
||||||
"opertype": "1",
|
"opertype": "1",
|
||||||
"fname": file.GetName(),
|
"fname": safeName,
|
||||||
}).SetMultipartField("Filedata", file.GetName(), file.GetMimetype(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
|
}).SetMultipartField("Filedata", safeName, file.GetMimetype(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -313,9 +339,10 @@ func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.F
|
|||||||
const DEFAULT int64 = 10485760
|
const DEFAULT int64 = 10485760
|
||||||
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||||
|
|
||||||
|
safeName := d.sanitizeName(file.GetName())
|
||||||
res, err := d.uploadRequest("/person/initMultiUpload", map[string]string{
|
res, err := d.uploadRequest("/person/initMultiUpload", map[string]string{
|
||||||
"parentFolderId": dstDir.GetID(),
|
"parentFolderId": dstDir.GetID(),
|
||||||
"fileName": encode(file.GetName()),
|
"fileName": encode(safeName),
|
||||||
"fileSize": strconv.FormatInt(file.GetSize(), 10),
|
"fileSize": strconv.FormatInt(file.GetSize(), 10),
|
||||||
"sliceSize": strconv.FormatInt(DEFAULT, 10),
|
"sliceSize": strconv.FormatInt(DEFAULT, 10),
|
||||||
"lazyCheck": "1",
|
"lazyCheck": "1",
|
||||||
|
|||||||
@@ -205,10 +205,11 @@ func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName s
|
|||||||
fullUrl += "/createFolder.action"
|
fullUrl += "/createFolder.action"
|
||||||
|
|
||||||
var newFolder Cloud189Folder
|
var newFolder Cloud189Folder
|
||||||
|
safeName := y.sanitizeName(dirName)
|
||||||
_, err := y.post(fullUrl, func(req *resty.Request) {
|
_, err := y.post(fullUrl, func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
req.SetContext(ctx)
|
||||||
req.SetQueryParams(map[string]string{
|
req.SetQueryParams(map[string]string{
|
||||||
"folderName": dirName,
|
"folderName": safeName,
|
||||||
"relativePath": "",
|
"relativePath": "",
|
||||||
})
|
})
|
||||||
if isFamily {
|
if isFamily {
|
||||||
@@ -225,6 +226,7 @@ func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
newFolder.Name = safeName
|
||||||
return &newFolder, nil
|
return &newFolder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,21 +260,29 @@ func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
var newObj model.Obj
|
var newObj model.Obj
|
||||||
|
safeName := y.sanitizeName(newName)
|
||||||
switch f := srcObj.(type) {
|
switch f := srcObj.(type) {
|
||||||
case *Cloud189File:
|
case *Cloud189File:
|
||||||
fullUrl += "/renameFile.action"
|
fullUrl += "/renameFile.action"
|
||||||
queryParam["fileId"] = srcObj.GetID()
|
queryParam["fileId"] = srcObj.GetID()
|
||||||
queryParam["destFileName"] = newName
|
queryParam["destFileName"] = safeName
|
||||||
newObj = &Cloud189File{Icon: f.Icon} // 复用预览
|
newObj = &Cloud189File{Icon: f.Icon} // 复用预览
|
||||||
case *Cloud189Folder:
|
case *Cloud189Folder:
|
||||||
fullUrl += "/renameFolder.action"
|
fullUrl += "/renameFolder.action"
|
||||||
queryParam["folderId"] = srcObj.GetID()
|
queryParam["folderId"] = srcObj.GetID()
|
||||||
queryParam["destFolderName"] = newName
|
queryParam["destFolderName"] = safeName
|
||||||
newObj = &Cloud189Folder{}
|
newObj = &Cloud189Folder{}
|
||||||
default:
|
default:
|
||||||
return nil, errs.NotSupport
|
return nil, errs.NotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch obj := newObj.(type) {
|
||||||
|
case *Cloud189File:
|
||||||
|
obj.Name = safeName
|
||||||
|
case *Cloud189Folder:
|
||||||
|
obj.Name = safeName
|
||||||
|
}
|
||||||
|
|
||||||
_, err := y.request(fullUrl, method, func(req *resty.Request) {
|
_, err := y.request(fullUrl, method, func(req *resty.Request) {
|
||||||
req.SetContext(ctx).SetQueryParams(queryParam)
|
req.SetContext(ctx).SetQueryParams(queryParam)
|
||||||
}, nil, newObj, isFamily)
|
}, nil, newObj, isFamily)
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
Username string `json:"username" required:"true"`
|
Username string `json:"username" required:"true"`
|
||||||
Password string `json:"password" required:"true"`
|
Password string `json:"password" required:"true"`
|
||||||
VCode string `json:"validate_code"`
|
VCode string `json:"validate_code"`
|
||||||
|
StripEmoji bool `json:"strip_emoji" help:"Remove four-byte characters (e.g., emoji) before upload"`
|
||||||
driver.RootID
|
driver.RootID
|
||||||
OrderBy string `json:"order_by" type:"select" options:"filename,filesize,lastOpTime" default:"filename"`
|
OrderBy string `json:"order_by" type:"select" options:"filename,filesize,lastOpTime" default:"filename"`
|
||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ import (
|
|||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"golang.org/x/sync/semaphore"
|
"golang.org/x/sync/semaphore"
|
||||||
|
|
||||||
@@ -57,6 +59,29 @@ const (
|
|||||||
CHANNEL_ID = "web_cloud.189.cn"
|
CHANNEL_ID = "web_cloud.189.cn"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (y *Cloud189PC) sanitizeName(name string) string {
|
||||||
|
if !y.StripEmoji {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
b := strings.Builder{}
|
||||||
|
for _, r := range name {
|
||||||
|
if utf8.RuneLen(r) == 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
sanitized := b.String()
|
||||||
|
if sanitized == "" {
|
||||||
|
ext := path.Ext(name)
|
||||||
|
if ext != "" {
|
||||||
|
sanitized = "file" + ext
|
||||||
|
} else {
|
||||||
|
sanitized = "file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitized
|
||||||
|
}
|
||||||
|
|
||||||
func (y *Cloud189PC) SignatureHeader(url, method, params string, isFamily bool) map[string]string {
|
func (y *Cloud189PC) SignatureHeader(url, method, params string, isFamily bool) map[string]string {
|
||||||
dateOfGmt := getHttpDateStr()
|
dateOfGmt := getHttpDateStr()
|
||||||
sessionKey := y.getTokenInfo().SessionKey
|
sessionKey := y.getTokenInfo().SessionKey
|
||||||
@@ -475,10 +500,11 @@ func (y *Cloud189PC) refreshSession() (err error) {
|
|||||||
func (y *Cloud189PC) StreamUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress, isFamily bool, overwrite bool) (model.Obj, error) {
|
func (y *Cloud189PC) StreamUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress, isFamily bool, overwrite bool) (model.Obj, error) {
|
||||||
size := file.GetSize()
|
size := file.GetSize()
|
||||||
sliceSize := partSize(size)
|
sliceSize := partSize(size)
|
||||||
|
safeName := y.sanitizeName(file.GetName())
|
||||||
|
|
||||||
params := Params{
|
params := Params{
|
||||||
"parentFolderId": dstDir.GetID(),
|
"parentFolderId": dstDir.GetID(),
|
||||||
"fileName": url.QueryEscape(file.GetName()),
|
"fileName": url.QueryEscape(safeName),
|
||||||
"fileSize": fmt.Sprint(file.GetSize()),
|
"fileSize": fmt.Sprint(file.GetSize()),
|
||||||
"sliceSize": fmt.Sprint(sliceSize),
|
"sliceSize": fmt.Sprint(sliceSize),
|
||||||
"lazyCheck": "1",
|
"lazyCheck": "1",
|
||||||
@@ -596,7 +622,8 @@ func (y *Cloud189PC) RapidUpload(ctx context.Context, dstDir model.Obj, stream m
|
|||||||
return nil, errors.New("invalid hash")
|
return nil, errors.New("invalid hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadInfo, err := y.OldUploadCreate(ctx, dstDir.GetID(), fileMd5, stream.GetName(), fmt.Sprint(stream.GetSize()), isFamily)
|
safeName := y.sanitizeName(stream.GetName())
|
||||||
|
uploadInfo, err := y.OldUploadCreate(ctx, dstDir.GetID(), fileMd5, safeName, fmt.Sprint(stream.GetSize()), isFamily)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -615,6 +642,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
|||||||
tmpF *os.File
|
tmpF *os.File
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
safeName := y.sanitizeName(file.GetName())
|
||||||
size := file.GetSize()
|
size := file.GetSize()
|
||||||
if _, ok := cache.(io.ReaderAt); !ok && size > 0 {
|
if _, ok := cache.(io.ReaderAt); !ok && size > 0 {
|
||||||
tmpF, err = os.CreateTemp(conf.Conf.TempDir, "file-*")
|
tmpF, err = os.CreateTemp(conf.Conf.TempDir, "file-*")
|
||||||
@@ -697,7 +725,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
|||||||
//step.2 预上传
|
//step.2 预上传
|
||||||
params := Params{
|
params := Params{
|
||||||
"parentFolderId": dstDir.GetID(),
|
"parentFolderId": dstDir.GetID(),
|
||||||
"fileName": url.QueryEscape(file.GetName()),
|
"fileName": url.QueryEscape(safeName),
|
||||||
"fileSize": fmt.Sprint(file.GetSize()),
|
"fileSize": fmt.Sprint(file.GetSize()),
|
||||||
"fileMd5": fileMd5Hex,
|
"fileMd5": fileMd5Hex,
|
||||||
"sliceSize": fmt.Sprint(sliceSize),
|
"sliceSize": fmt.Sprint(sliceSize),
|
||||||
@@ -833,9 +861,10 @@ func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rateLimited := driver.NewLimitedUploadStream(ctx, io.NopCloser(tempFile))
|
rateLimited := driver.NewLimitedUploadStream(ctx, io.NopCloser(tempFile))
|
||||||
|
safeName := y.sanitizeName(file.GetName())
|
||||||
|
|
||||||
// 创建上传会话
|
// 创建上传会话
|
||||||
uploadInfo, err := y.OldUploadCreate(ctx, dstDir.GetID(), fileMd5, file.GetName(), fmt.Sprint(file.GetSize()), isFamily)
|
uploadInfo, err := y.OldUploadCreate(ctx, dstDir.GetID(), fileMd5, safeName, fmt.Sprint(file.GetSize()), isFamily)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user