Skip to content

Commit 0a3f3e1

Browse files
mattndsnet
authored andcommitted
archive/zip: set utf-8 flag
See: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.0.TXT Document says: > If general purpose bit 11 is set, the filename and comment must support The > Unicode Standard, Version 4.1.0 or greater using the character encoding form > defined by the UTF-8 storage specification. Since Go encode the filename to UTF-8, general purpose bit 11 should be set. Change-Id: Ica4af02b4dc695e9a5c015ae360e70171efb6ee3 Reviewed-on: https://go-review.googlesource.com/39570 Reviewed-by: Joe Tsai <[email protected]> Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent a8dd20d commit 0a3f3e1

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

src/archive/zip/writer.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"hash"
1212
"hash/crc32"
1313
"io"
14+
"unicode/utf8"
1415
)
1516

1617
// TODO(adg): support zip file comments
@@ -201,6 +202,20 @@ func (w *Writer) Create(name string) (io.Writer, error) {
201202
return w.CreateHeader(header)
202203
}
203204

205+
func hasValidUTF8(s string) bool {
206+
n := 0
207+
for _, r := range s {
208+
// By default, ZIP uses CP437, which is only identical to ASCII for the printable characters.
209+
if r < 0x20 || r >= 0x7f {
210+
if !utf8.ValidRune(r) {
211+
return false
212+
}
213+
n++
214+
}
215+
}
216+
return n > 0
217+
}
218+
204219
// CreateHeader adds a file to the zip file using the provided FileHeader
205220
// for the file metadata.
206221
// It returns a Writer to which the file contents should be written.
@@ -221,6 +236,10 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
221236

222237
fh.Flags |= 0x8 // we will write a data descriptor
223238

239+
if hasValidUTF8(fh.Name) || hasValidUTF8(fh.Comment) {
240+
fh.Flags |= 0x800 // filename or comment have valid utf-8 string
241+
}
242+
224243
fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
225244
fh.ReaderVersion = zipVersion20
226245

src/archive/zip/writer_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,69 @@ func TestWriter(t *testing.T) {
8787
}
8888
}
8989

90+
func TestWriterUTF8(t *testing.T) {
91+
var utf8Tests = []struct {
92+
name string
93+
comment string
94+
expect uint16
95+
}{
96+
{
97+
name: "hi, hello",
98+
comment: "in the world",
99+
expect: 0x8,
100+
},
101+
{
102+
name: "hi, こんにちわ",
103+
comment: "in the world",
104+
expect: 0x808,
105+
},
106+
{
107+
name: "hi, hello",
108+
comment: "in the 世界",
109+
expect: 0x808,
110+
},
111+
{
112+
name: "hi, こんにちわ",
113+
comment: "in the 世界",
114+
expect: 0x808,
115+
},
116+
}
117+
118+
// write a zip file
119+
buf := new(bytes.Buffer)
120+
w := NewWriter(buf)
121+
122+
for _, test := range utf8Tests {
123+
h := &FileHeader{
124+
Name: test.name,
125+
Comment: test.comment,
126+
Method: Deflate,
127+
}
128+
w, err := w.CreateHeader(h)
129+
if err != nil {
130+
t.Fatal(err)
131+
}
132+
w.Write([]byte{})
133+
}
134+
135+
if err := w.Close(); err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
// read it back
140+
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
141+
if err != nil {
142+
t.Fatal(err)
143+
}
144+
for i, test := range utf8Tests {
145+
got := r.File[i].Flags
146+
t.Logf("name %v, comment %v", test.name, test.comment)
147+
if got != test.expect {
148+
t.Fatalf("Flags: got %v, want %v", got, test.expect)
149+
}
150+
}
151+
}
152+
90153
func TestWriterOffset(t *testing.T) {
91154
largeData := make([]byte, 1<<17)
92155
for i := range largeData {

0 commit comments

Comments
 (0)