webdav: define property system and implement PROPFIND.

This change adds support for PROPFIND requests to net/webdav.
It contains a proposed PropSystem interface and a preliminary
implementation of an in-memory property system. As discussed
with nigeltao, this is the first of approximately 4-5 CLs to
get property support in the net/webdav package.

Current coverage of litmus 'props' test suite:
16 tests were skipped, 14 tests run. 10 passed, 4 failed. 71.4%

Change-Id: I0bc5f375422137e911a2f6fb0e99c43a5a52d5ac
Reviewed-on: https://go-review.googlesource.com/3417
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/webdav/file.go b/webdav/file.go
index aa4ea6a..2f62dc4 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -672,3 +672,54 @@
 	}
 	return http.StatusNoContent, nil
 }
+
+// walkFS traverses filesystem fs starting at path up to depth levels.
+//
+// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
+// walkFS calls walkFn. If a visited file system node is a directory and
+// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
+func walkFS(fs FileSystem, depth int, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+	// This implementation is based on Walk's code in the standard path/filepath package.
+	err := walkFn(path, info, nil)
+	if err != nil {
+		if info.IsDir() && err == filepath.SkipDir {
+			return nil
+		}
+		return err
+	}
+	if !info.IsDir() || depth == 0 {
+		return nil
+	}
+	if depth == 1 {
+		depth = 0
+	}
+
+	// Read directory names.
+	f, err := fs.OpenFile(path, os.O_RDONLY, 0)
+	if err != nil {
+		return walkFn(path, info, err)
+	}
+	fileInfos, err := f.Readdir(0)
+	f.Close()
+	if err != nil {
+		return walkFn(path, info, err)
+	}
+
+	for _, fileInfo := range fileInfos {
+		filename := filepath.Join(path, fileInfo.Name())
+		fileInfo, err := fs.Stat(filename)
+		if err != nil {
+			if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
+				return err
+			}
+		} else {
+			err = walkFS(fs, depth, filename, fileInfo, walkFn)
+			if err != nil {
+				if !fileInfo.IsDir() || err != filepath.SkipDir {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}