Skip to content
This repository was archived by the owner on Mar 29, 2023. It is now read-only.

multipart: fix handling of common prefixes #7

Merged
merged 1 commit into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions multifilereader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,43 @@ func TestOutput(t *testing.T) {
t.Fatal("Expected to get (nil, io.EOF)")
}
}

func TestCommonPrefix(t *testing.T) {
sf := NewMapDirectory(map[string]Node{
"boop": NewMapDirectory(map[string]Node{
"a": NewBytesFile([]byte("bleep")),
"aa": NewBytesFile([]byte("bleep")),
"aaa": NewBytesFile([]byte("bleep")),
}),
})
mfr := NewMultiFileReader(sf, true)
reader, err := NewFileFromPartReader(multipart.NewReader(mfr, mfr.Boundary()), multipartFormdataType)
if err != nil {
t.Fatal(err)
}

CheckDir(t, reader, []Event{
{
kind: TDirStart,
name: "boop",
},
{
kind: TFile,
name: "a",
value: "bleep",
},
{
kind: TFile,
name: "aa",
value: "bleep",
},
{
kind: TFile,
name: "aaa",
value: "bleep",
},
{
kind: TDirEnd,
},
})
}
29 changes: 26 additions & 3 deletions multipartfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func (w *multipartWalker) nextFile() (Node, error) {
}, nil
}

// fileName returns a normalized filename from a part.
func fileName(part *multipart.Part) string {
filename := part.FileName()
if escaped, err := url.QueryUnescape(filename); err == nil {
Expand All @@ -120,10 +121,32 @@ func fileName(part *multipart.Part) string {
return path.Clean("/" + filename)
}

// dirName appends a slash to the end of the filename, if not present.
// expects a _cleaned_ path.
func dirName(filename string) string {
if !strings.HasSuffix(filename, "/") {
filename += "/"
}
return filename
}

// isDirectory checks if the media type is a valid directory media type.
func isDirectory(mediatype string) bool {
return mediatype == multipartFormdataType || mediatype == applicationDirectory
}

// isChild checks if child is a child of parent directory.
// expects a _cleaned_ path.
func isChild(child, parent string) bool {
return strings.HasPrefix(child, dirName(parent))
}

// makeRelative makes the child path relative to the parent path.
// expects a _cleaned_ path.
func makeRelative(child, parent string) string {
return strings.TrimPrefix(child, dirName(parent))
}

type multipartIterator struct {
f *multipartDirectory

Expand Down Expand Up @@ -154,18 +177,18 @@ func (it *multipartIterator) Next() bool {
name := fileName(part)

// Is the file in a different directory?
if !strings.HasPrefix(name, it.f.path) {
if !isChild(name, it.f.path) {
return false
}

// Have we already entered this directory?
if it.curName != "" && strings.HasPrefix(name, path.Join(it.f.path, it.curName)) {
if it.curName != "" && isChild(name, path.Join(it.f.path, it.curName)) {
it.f.walker.consumePart()
continue
}

// Make the path relative to the current directory.
name = strings.TrimLeft(name[len(it.f.path):], "/")
name = makeRelative(name, it.f.path)

// Check if we need to create a fake directory (more than one
// path component).
Expand Down