Skip to content

fix(drivers/189pc): normalize escaped apostrophes in names#2792

Open
qbisi wants to merge 1 commit into
OpenListTeam:mainfrom
qbisi:fix/189cloud-apostrophe-names
Open

fix(drivers/189pc): normalize escaped apostrophes in names#2792
qbisi wants to merge 1 commit into
OpenListTeam:mainfrom
qbisi:fix/189cloud-apostrophe-names

Conversation

@qbisi

@qbisi qbisi commented Jul 15, 2026

Copy link
Copy Markdown

Summary / 摘要

189Cloud 不接受文件名中的英文单引号,并可能将单引号以 \' 的形式返回。OpenList 原先会把该云端名称传入通用路径清理逻辑,其中的反斜杠会被解释为路径分隔符,最终导致对象无法访问。

本 PR 在现有名称包装边界引入可选的 ObjDisplayName

  • Cloud189FileCloud189FolderGetName() 继续返回 189Cloud 原始名称,确保移动、复制、重命名、删除等驱动操作获得未经修改的云端对象信息。
  • 两类对象通过 GetDisplayName() 仅将 \' 转换为 ',供 OpenList 路径匹配、目录缓存和用户界面使用。
  • WrapObjName() 优先读取可选展示名,再执行既有的全局 utils.MappingName();包装对象内部仍保存原始对象,现有 UnwrapObjName() 写操作流程不变。
  • WrapObjsName() 统一复用 WrapObjName(),避免单对象与列表对象采用不同的包装规则。
  • 对所有未实现 ObjDisplayName 的现有对象,WrapObjName() 仍使用 GetName(),行为与修改前完全一致。
  • 不涉及配置、存储格式、公开 API 或驱动写操作的变更。

之所以修改中央 WrapObjName(),而不是直接修改 189CloudPC 的 GetName(),是因为后者同时被 189Cloud 批处理接口用作原始 FileName。直接替换会把展示名称发送回云端,可能影响移动、复制、删除、家庭云转存和 torrent sidecar 操作。将转换限制在既有包装边界,可以维持“展示名称”和“云端原始名称”的分离。

历史实现也支持这一边界:

  • 83644dab 曾将文件名映射直接放入多个对象的 GetName(),包括 189CloudPC。

  • fb64f006 随后将映射从原始对象移至 ObjWrapName,并通过 unwrap 机制确保驱动操作继续获得原始对象。本 PR 延续这一设计。

  • 05898d90 进一步处理了展示名称无法反向转换时与 driver.Getter 的冲突,说明名称映射应当明确限制在可安全包装的边界。

  • This PR has breaking changes.
    / 此 PR 包含破坏性变更。

  • This PR changes public API, config, storage format, or migration behavior.
    / 此 PR 修改了公开 API、配置、存储格式或迁移行为。

  • This PR requires corresponding changes in related repositories.
    / 此 PR 需要关联仓库同步修改。

Related repository PRs / 关联仓库 PR:

  • OpenList-Frontend: N/A
  • OpenList-Docs: N/A

Testing / 测试

  • go test ./...
  • CGO_ENABLED=0 go test -vet=off ./drivers/189pc ./internal/model ./internal/op ./internal/fs ./pkg/utils
  • Manual test / 手动测试: Confirmed against a live 189CloudPC storage that the normalized apostrophe path can be listed and opened. The previous escaped form is transformed into a path separator and fails, reproducing the root cause. The branch was deployed and confirmed to fix the affected file access.

The standard targeted go test invocation is currently blocked by pre-existing dynamic-format fmt.Errorf vet findings in drivers/189pc/utils.go; the same packages pass with vet disabled.

Checklist / 检查清单

  • I have read CONTRIBUTING.
    / 我已阅读 CONTRIBUTING
  • I confirm this contribution follows the repository license, contribution policy, and code of conduct.
    / 我确认此贡献符合仓库许可证、贡献规范和行为准则。
  • I have formatted the changed code with gofmt, go fmt, or prettier where applicable.
    / 我已按适用情况使用 gofmtgo fmtprettier 格式化变更代码。
  • I have requested review from relevant maintainers or code owners where applicable.
    / 我已在适用情况下请求相关维护者或代码所有者审查。

AI Disclosure / AI 使用声明

  • This PR includes AI-assisted content.
    / 此 PR 包含 AI 辅助内容。

Tools used / 使用工具:

  • ChatGPT
  • Codex
  • GitHub Copilot
  • Claude
  • Gemini
  • Other (please specify) / 其他(请注明):

Usage scope / 使用范围:

  • Code generation / 代码生成

  • Refactoring / 重构

  • Documentation / 文档

  • Tests / 测试

  • Translation / 翻译

  • Review assistance / 审查辅助

  • I have reviewed and validated all AI-assisted content included in this PR.
    / 我已审核并验证此 PR 中的所有 AI 辅助内容。

  • I have ensured that all AI-assisted commits include Co-Authored-By attribution.
    / 我已确保所有 AI 辅助提交都包含 Co-Authored-By 归属信息。

  • I can reproduce all AI-assisted content included in this PR without any AI tools.
    / 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。

jyxjjj
jyxjjj previously requested changes Jul 15, 2026

@jyxjjj jyxjjj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For such a small change, there’s no need to introduce this many layers of abstraction. This looks like AI-generated over-engineering rather than something justified by the requirements. I’d recommend simplifying it and keeping the implementation straightforward.

@qbisi

qbisi commented Jul 15, 2026

Copy link
Copy Markdown
Author

确实,改动跨模块太多了,先draft

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
@qbisi qbisi force-pushed the fix/189cloud-apostrophe-names branch from 9bcae12 to 603e97b Compare July 15, 2026 01:14
@qbisi qbisi changed the title fix(189pc): normalize escaped apostrophes in names fix(drivers/189pc): normalize escaped apostrophes in names Jul 15, 2026
@qbisi

qbisi commented Jul 15, 2026

Copy link
Copy Markdown
Author

已通过openlist手动测试复制,改名,删除操作成功, 手机验证天翼云盘客户端达到预期改动。

@qbisi qbisi marked this pull request as ready for review July 15, 2026 01:54
jyxjjj added a commit to qbisi/OpenList that referenced this pull request Jul 15, 2026
- Inline escaped apostrophe normalization in object name wrappers
- Remove driver-specific display name helpers and interface
- Preserve separate single-object and batch-object wrapping paths

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
@jyxjjj jyxjjj dismissed their stale review July 15, 2026 02:05

done

jyxjjj added a commit to qbisi/OpenList that referenced this pull request Jul 15, 2026
@qbisi

qbisi commented Jul 15, 2026

Copy link
Copy Markdown
Author

如果以后有需要,就在 WrapObjsName 和 getName 增加一个map函数让driver 实现,默认是同名变换。

@jyxjjj jyxjjj force-pushed the fix/189cloud-apostrophe-names branch from 8398a75 to 603e97b Compare July 15, 2026 02:13
@jyxjjj

jyxjjj commented Jul 15, 2026

Copy link
Copy Markdown
Member

尝试改了一下发现全放model影响其他驱动 而抽象的又确实过度 我再想想怎么才够优雅

@jyxjjj

jyxjjj commented Jul 15, 2026

Copy link
Copy Markdown
Member
diff --git a/drivers/189pc/types.go b/drivers/189pc/types.go
index ab99db2b..c37ea3df 100644
--- a/drivers/189pc/types.go
+++ b/drivers/189pc/types.go
@@ -191,10 +191,6 @@ type Cloud189File struct {
 	// StarLabel   int64  `json:"starLabel"`
 }
 
-func normalizeCloud189Name(name string) string {
-	return strings.ReplaceAll(name, "\\'", "'")
-}
-
 func (c *Cloud189File) CreateTime() time.Time {
 	return time.Time(c.CreateDate)
 }
@@ -211,7 +207,9 @@ func (c *Cloud189File) GetID() string      { return string(c.ID) }
 func (c *Cloud189File) GetPath() string    { return "" }
 func (c *Cloud189File) Thumb() string      { return c.Icon.SmallUrl }
 
-func (c *Cloud189File) GetDisplayName() string { return normalizeCloud189Name(c.Name) }
+func (c *Cloud189File) GetDisplayName() string {
+	return strings.ReplaceAll(c.Name, "\\'", "'")
+}
 
 // 文件夹
 type Cloud189Folder struct {
@@ -244,7 +242,9 @@ func (c *Cloud189Folder) IsDir() bool        { return true }
 func (c *Cloud189Folder) GetID() string      { return string(c.ID) }
 func (c *Cloud189Folder) GetPath() string    { return "" }
 
-func (c *Cloud189Folder) GetDisplayName() string { return normalizeCloud189Name(c.Name) }
+func (c *Cloud189Folder) GetDisplayName() string {
+	return strings.ReplaceAll(c.Name, "\\'", "'")
+}
 
 type Cloud189FilesResp struct {
 	//ResCode    int    `json:"res_code"`
diff --git a/internal/model/obj.go b/internal/model/obj.go
index 1269b5b7..c282923b 100644
--- a/internal/model/obj.go
+++ b/internal/model/obj.go
@@ -19,10 +19,6 @@ type ObjUnwrap interface {
 	Unwrap() Obj
 }
 
-type ObjDisplayName interface {
-	GetDisplayName() string
-}
-
 type Obj interface {
 	GetSize() int64
 	GetName() string
@@ -138,7 +134,7 @@ func ExtractFolder(objs []Obj, extractFolder string) {
 
 func WrapObjName(obj Obj) Obj {
 	name := obj.GetName()
-	if displayName, ok := obj.(ObjDisplayName); ok {
+	if displayName, ok := obj.(interface{ GetDisplayName() string }); ok {
 		name = displayName.GetDisplayName()
 	}
 	return &ObjWrapName{Name: utils.MappingName(name), Obj: obj}

@j2rong4cn 帮忙看下,我觉得这个 normalizeCloud189Name 确实不够优雅,但我这个版本实际上只是把抽象藏进了匿名接口里,还重复了两遍替换逻辑,可能只是代码更少,不一定更清晰。你觉得哪个方案更合适?

@qbisi

qbisi commented Jul 15, 2026

Copy link
Copy Markdown
Author

我同意 model/obj.go 里先不增加 type ObjDisplayName interface 。直到有更多的网盘出现 WrapObjName 和 Name 不一致的情况,再决定下一步抽象

@j2rong4cn

j2rong4cn commented Jul 15, 2026

Copy link
Copy Markdown
Member

默认情况下utils.MappingName只是把/替换成|,但哪些驱动支持/作为文件名?我之前就想过把全局文件名映射移到驱动层,留个接口给驱动,只在驱动实现该接口了再处理文件名

@j2rong4cn

Copy link
Copy Markdown
Member

现在最重要的是能跑就行,

@jyxjjj

jyxjjj commented Jul 15, 2026

Copy link
Copy Markdown
Member

那要不然直接Breaking,全部都改,用户已有文件的话发公告自行处理

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants