diff --git a/internal/protocol/http/fetcher.go b/internal/protocol/http/fetcher.go index 0405319f..a4b493ad 100644 --- a/internal/protocol/http/fetcher.go +++ b/internal/protocol/http/fetcher.go @@ -23,6 +23,7 @@ import ( "github.com/GopeedLab/gopeed/internal/fetcher" "github.com/GopeedLab/gopeed/pkg/base" fhttp "github.com/GopeedLab/gopeed/pkg/protocol/http" + "github.com/GopeedLab/gopeed/pkg/util" "github.com/xiaoqidun/setft" "golang.org/x/sync/errgroup" ) @@ -172,13 +173,13 @@ func (f *Fetcher) Resolve(req *base.Request) error { filename = strings.Replace(filename, "UTF8", "UTF-8", 1) file.Name, _ = decoder.Decode(filename) } else { - file.Name = filename + file.Name = util.TryUrlQueryUnescape(filename) } } else { substr := "attachment; filename=" index := strings.Index(contentDisposition, substr) if index != -1 { - file.Name = contentDisposition[index+len(substr):] + file.Name = util.TryUrlQueryUnescape(contentDisposition[index+len(substr):]) } } } diff --git a/pkg/util/url.go b/pkg/util/url.go index 07c87be5..6007469b 100644 --- a/pkg/util/url.go +++ b/pkg/util/url.go @@ -56,3 +56,13 @@ func ProxyUrlToHandler(proxyUrl *url.URL) func(*http.Request) (*url.URL, error) } return http.ProxyURL(proxyUrl) } + +// TryUrlQueryUnescape tries to unescape a URL-encoded string. +// +// If unescaping fails, it returns the original string. +func TryUrlQueryUnescape(s string) string { + if decoded, err := url.QueryUnescape(s); err == nil { + return decoded + } + return s +} diff --git a/pkg/util/url_test.go b/pkg/util/url_test.go index 645b4876..74ee75d4 100644 --- a/pkg/util/url_test.go +++ b/pkg/util/url_test.go @@ -128,3 +128,24 @@ func TestParseDataUri(t *testing.T) { }) } } + +func TestTryURLDecode(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"normal.txt", "normal.txt"}, + {"%E7%8F%80%E5%B0%94%E8%AF%BA.zip", "珀尔诺.zip"}, + {"hello%20world.txt", "hello world.txt"}, + {"bad%2-text", "bad%2-text"}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := TryUrlQueryUnescape(tt.input) + if got != tt.expected { + t.Errorf("TryUrlQueryUnescape(%q) = %q, want %q", tt.input, got, tt.expected) + } + }) + } +}