Finish the first version of propfind.
This commit is contained in:
267
build/doc/webdav/PROPFIND.md
Normal file
267
build/doc/webdav/PROPFIND.md
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
#### PROPFIND 列出目录情况
|
||||||
|
request method
|
||||||
|
```
|
||||||
|
PROPFIND
|
||||||
|
```
|
||||||
|
|
||||||
|
request header
|
||||||
|
```
|
||||||
|
Authorization=Basic YWRtaW46YWRtaW4=
|
||||||
|
Content-Type=text/xml
|
||||||
|
Accept-Encoding=gzip
|
||||||
|
Depth=infinity
|
||||||
|
```
|
||||||
|
|
||||||
|
request body
|
||||||
|
```
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<D:propfind xmlns:D="DAV:">
|
||||||
|
<D:prop>
|
||||||
|
<D:resourcetype />
|
||||||
|
<D:getcontentlength />
|
||||||
|
<D:creationdate />
|
||||||
|
<D:getlastmodified />
|
||||||
|
</D:prop>
|
||||||
|
</D:propfind>
|
||||||
|
```
|
||||||
|
|
||||||
|
response body
|
||||||
|
```
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<D:multistatus xmlns:D="DAV:">
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:displayname>dav</D:displayname>
|
||||||
|
<D:getlastmodified>Tue, 16 Apr 2019 17:50:59 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/api/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:displayname>api</D:displayname>
|
||||||
|
<D:getlastmodified>Tue, 16 Apr 2019 17:51:03 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/api/dav/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:displayname>dav</D:displayname>
|
||||||
|
<D:getlastmodified>Tue, 16 Apr 2019 17:51:38 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/api/dav/body.txt</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:displayname>body.txt</D:displayname>
|
||||||
|
<D:getlastmodified>Tue, 16 Apr 2019 17:51:38 GMT</D:getlastmodified>
|
||||||
|
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||||
|
<D:getetag>"159605ccc1d0f3c410"</D:getetag>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:resourcetype></D:resourcetype>
|
||||||
|
<D:getcontentlength>16</D:getcontentlength>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/api/dav/cat.txt</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:resourcetype></D:resourcetype>
|
||||||
|
<D:getcontentlength>24</D:getcontentlength>
|
||||||
|
<D:getlastmodified>Tue, 16 Apr 2019 17:51:19 GMT</D:getlastmodified>
|
||||||
|
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||||
|
<D:getetag>"159605c862b0d64c18"</D:getetag>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:displayname>cat.txt</D:displayname>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/cat/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:getlastmodified>Sat, 13 Apr 2019 16:55:54 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:displayname>cat</D:displayname>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/cat/dog/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:getlastmodified>Sat, 13 Apr 2019 16:55:58 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:displayname>dog</D:displayname>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/cat/dog/pig/</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:resourcetype>
|
||||||
|
<D:collection xmlns:D="DAV:"/>
|
||||||
|
</D:resourcetype>
|
||||||
|
<D:getlastmodified>Sat, 13 Apr 2019 16:56:08 GMT</D:getlastmodified>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:displayname>pig</D:displayname>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/cat/dog/pig/hi.txt</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:getlastmodified>Sat, 13 Apr 2019 16:56:08 GMT</D:getlastmodified>
|
||||||
|
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||||
|
<D:getetag>"15951707dc1116d87"</D:getetag>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
<D:displayname>hi.txt</D:displayname>
|
||||||
|
<D:resourcetype></D:resourcetype>
|
||||||
|
<D:getcontentlength>7</D:getcontentlength>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
<D:response>
|
||||||
|
<D:href>/api/dav/morning.txt</D:href>
|
||||||
|
<D:propstat>
|
||||||
|
<D:prop>
|
||||||
|
<D:resourcetype></D:resourcetype>
|
||||||
|
<D:getcontentlength>13</D:getcontentlength>
|
||||||
|
<D:displayname>morning.txt</D:displayname>
|
||||||
|
<D:getlastmodified>Sat, 13 Apr 2019 16:52:08 GMT</D:getlastmodified>
|
||||||
|
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||||
|
<D:getetag>"159516cfe790beecd"</D:getetag>
|
||||||
|
<D:supportedlock>
|
||||||
|
<D:lockentry xmlns:D="DAV:">
|
||||||
|
<D:lockscope>
|
||||||
|
<D:exclusive/>
|
||||||
|
</D:lockscope>
|
||||||
|
<D:locktype>
|
||||||
|
<D:write/>
|
||||||
|
</D:locktype>
|
||||||
|
</D:lockentry>
|
||||||
|
</D:supportedlock>
|
||||||
|
</D:prop>
|
||||||
|
<D:status>HTTP/1.1 200 OK</D:status>
|
||||||
|
</D:propstat>
|
||||||
|
</D:response>
|
||||||
|
</D:multistatus>
|
||||||
|
```
|
@ -17,9 +17,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// slashClean is equivalent to but slightly more efficient than
|
// SlashClean is equivalent to but slightly more efficient than
|
||||||
// path.Clean("/" + name).
|
// path.Clean("/" + name).
|
||||||
func slashClean(name string) string {
|
func SlashClean(name string) string {
|
||||||
if name == "" || name[0] != '/' {
|
if name == "" || name[0] != '/' {
|
||||||
name = "/" + name
|
name = "/" + name
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func (d Dir) resolve(name string) string {
|
|||||||
if dir == "" {
|
if dir == "" {
|
||||||
dir = "."
|
dir = "."
|
||||||
}
|
}
|
||||||
return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
|
return filepath.Join(dir, filepath.FromSlash(SlashClean(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
|
func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
|
||||||
@ -166,7 +166,7 @@ type memFS struct {
|
|||||||
// ends at that root node.
|
// ends at that root node.
|
||||||
func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
|
func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
|
||||||
original := fullname
|
original := fullname
|
||||||
fullname = slashClean(fullname)
|
fullname = SlashClean(fullname)
|
||||||
|
|
||||||
// Strip any leading "/"s to make fullname a relative path, as the walk
|
// Strip any leading "/"s to make fullname a relative path, as the walk
|
||||||
// starts at fs.root.
|
// starts at fs.root.
|
||||||
@ -335,8 +335,8 @@ func (fs *memFS) Rename(ctx context.Context, oldName, newName string) error {
|
|||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
defer fs.mu.Unlock()
|
defer fs.mu.Unlock()
|
||||||
|
|
||||||
oldName = slashClean(oldName)
|
oldName = SlashClean(oldName)
|
||||||
newName = slashClean(newName)
|
newName = SlashClean(newName)
|
||||||
if oldName == newName {
|
if oldName == newName {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func TestSlashClean(t *testing.T) {
|
|||||||
"a/b/c",
|
"a/b/c",
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
got := slashClean(tc)
|
got := SlashClean(tc)
|
||||||
want := path.Clean("/" + tc)
|
want := path.Clean("/" + tc)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("tc=%q: got %q, want %q", tc, got, want)
|
t.Errorf("tc=%q: got %q, want %q", tc, got, want)
|
||||||
|
@ -151,12 +151,12 @@ func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condit
|
|||||||
|
|
||||||
var n0, n1 *memLSNode
|
var n0, n1 *memLSNode
|
||||||
if name0 != "" {
|
if name0 != "" {
|
||||||
if n0 = m.lookup(slashClean(name0), conditions...); n0 == nil {
|
if n0 = m.lookup(SlashClean(name0), conditions...); n0 == nil {
|
||||||
return nil, ErrConfirmationFailed
|
return nil, ErrConfirmationFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if name1 != "" {
|
if name1 != "" {
|
||||||
if n1 = m.lookup(slashClean(name1), conditions...); n1 == nil {
|
if n1 = m.lookup(SlashClean(name1), conditions...); n1 == nil {
|
||||||
return nil, ErrConfirmationFailed
|
return nil, ErrConfirmationFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
|
|||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
m.collectExpiredNodes(now)
|
m.collectExpiredNodes(now)
|
||||||
details.Root = slashClean(details.Root)
|
details.Root = SlashClean(details.Root)
|
||||||
|
|
||||||
if !m.canCreate(details.Root, details.ZeroDepth) {
|
if !m.canCreate(details.Root, details.ZeroDepth) {
|
||||||
return "", ErrLocked
|
return "", ErrLocked
|
||||||
|
@ -336,7 +336,7 @@ loop:
|
|||||||
return []Propstat{pstat}, nil
|
return []Propstat{pstat}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeXML(s string) string {
|
func EscapeXML(s string) string {
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
// As an optimization, if s contains only ASCII letters, digits or a
|
// As an optimization, if s contains only ASCII letters, digits or a
|
||||||
// few special characters, the escaped value is s itself and we don't
|
// few special characters, the escaped value is s itself and we don't
|
||||||
@ -364,11 +364,11 @@ func findResourceType(ctx context.Context, fs FileSystem, ls LockSystem, name st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findDisplayName(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
func findDisplayName(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
||||||
if slashClean(name) == "/" {
|
if SlashClean(name) == "/" {
|
||||||
// Hide the real name of a possibly prefixed root directory.
|
// Hide the real name of a possibly prefixed root directory.
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return escapeXML(fi.Name()), nil
|
return EscapeXML(fi.Name()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findContentLength(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
func findContentLength(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
||||||
|
@ -208,7 +208,7 @@ func TestEscapeXML(t *testing.T) {
|
|||||||
// These test cases aren't exhaustive, and there is more than one way to
|
// These test cases aren't exhaustive, and there is more than one way to
|
||||||
// escape e.g. a quot (as """ or """) or an apos. We presume that
|
// escape e.g. a quot (as """ or """) or an apos. We presume that
|
||||||
// the encoding/xml package tests xml.EscapeText more thoroughly. This test
|
// the encoding/xml package tests xml.EscapeText more thoroughly. This test
|
||||||
// here is just a sanity check for this package's escapeXML function, and
|
// here is just a sanity check for this package's EscapeXML function, and
|
||||||
// its attempt to provide a fast path (and avoid a bytes.Buffer allocation)
|
// its attempt to provide a fast path (and avoid a bytes.Buffer allocation)
|
||||||
// when escaping filenames is obviously a no-op.
|
// when escaping filenames is obviously a no-op.
|
||||||
testCases := map[string]string{
|
testCases := map[string]string{
|
||||||
@ -236,7 +236,7 @@ func TestEscapeXML(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for in, want := range testCases {
|
for in, want := range testCases {
|
||||||
if got := escapeXML(in); got != want {
|
if got := EscapeXML(in); got != want {
|
||||||
t.Errorf("in=%q: got %q, want %q", in, got, want)
|
t.Errorf("in=%q: got %q, want %q", in, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"tank/rest/dav"
|
"tank/rest/dav"
|
||||||
@ -80,7 +81,8 @@ func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *htt
|
|||||||
path := request.URL.Path
|
path := request.URL.Path
|
||||||
|
|
||||||
//匹配 /api/dav{subPath}
|
//匹配 /api/dav{subPath}
|
||||||
reg := regexp.MustCompile(`^/api/dav(.*)$`)
|
pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFFIX)
|
||||||
|
reg := regexp.MustCompile(pattern)
|
||||||
strs := reg.FindStringSubmatch(path)
|
strs := reg.FindStringSubmatch(path)
|
||||||
if len(strs) == 2 {
|
if len(strs) == 2 {
|
||||||
var f = func(writer http.ResponseWriter, request *http.Request) {
|
var f = func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
@ -1,3 +1,102 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"tank/rest/dav"
|
||||||
|
)
|
||||||
|
|
||||||
|
//访问前缀,这个是特殊入口
|
||||||
|
var WEBDAV_PREFFIX = "/api/dav"
|
||||||
|
|
||||||
|
//动态的文件属性
|
||||||
|
type LiveProp struct {
|
||||||
|
findFn func(matter *Matter) string
|
||||||
|
dir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//所有的动态属性定义及其值的获取方式
|
||||||
|
var LivePropMap = map[xml.Name]LiveProp{
|
||||||
|
{Space: "DAV:", Local: "resourcetype"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
if matter.Dir {
|
||||||
|
return `<D:collection xmlns:D="DAV:"/>`
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dir: true,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "displayname"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
if dav.SlashClean(matter.Name) == "/" {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return dav.EscapeXML(matter.Name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dir: true,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "getcontentlength"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
return strconv.FormatInt(matter.Size, 10)
|
||||||
|
},
|
||||||
|
dir: false,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "getlastmodified"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
return matter.UpdateTime.UTC().Format(http.TimeFormat)
|
||||||
|
},
|
||||||
|
// http://webdav.org/specs/rfc4918.html#PROPERTY_getlastmodified
|
||||||
|
// suggests that getlastmodified should only apply to GETable
|
||||||
|
// resources, and this package does not support GET on directories.
|
||||||
|
//
|
||||||
|
// Nonetheless, some WebDAV clients expect child directories to be
|
||||||
|
// sortable by getlastmodified date, so this value is true, not false.
|
||||||
|
// See golang.org/issue/15334.
|
||||||
|
dir: true,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "creationdate"}: {
|
||||||
|
findFn: nil,
|
||||||
|
dir: false,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "getcontentlanguage"}: {
|
||||||
|
findFn: nil,
|
||||||
|
dir: false,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "getcontenttype"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
if matter.Dir {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return dav.EscapeXML(matter.Name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dir: false,
|
||||||
|
},
|
||||||
|
{Space: "DAV:", Local: "getetag"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
return fmt.Sprintf(`"%x%x"`, matter.UpdateTime.UnixNano(), matter.Size)
|
||||||
|
},
|
||||||
|
// findETag implements ETag as the concatenated hex values of a file's
|
||||||
|
// modification time and size. This is not a reliable synchronization
|
||||||
|
// mechanism for directories, so we do not advertise getetag for DAV
|
||||||
|
// collections.
|
||||||
|
dir: false,
|
||||||
|
},
|
||||||
|
// TODO: The lockdiscovery property requires LockSystem to list the
|
||||||
|
// active locks on a resource.
|
||||||
|
{Space: "DAV:", Local: "lockdiscovery"}: {},
|
||||||
|
{Space: "DAV:", Local: "supportedlock"}: {
|
||||||
|
findFn: func(matter *Matter) string {
|
||||||
|
return `` +
|
||||||
|
`<D:lockentry xmlns:D="DAV:">` +
|
||||||
|
`<D:lockscope><D:exclusive/></D:lockscope>` +
|
||||||
|
`<D:locktype><D:write/></D:locktype>` +
|
||||||
|
`</D:lockentry>`
|
||||||
|
},
|
||||||
|
dir: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -58,6 +58,48 @@ func (this *DavService) PropNames(matter *Matter) []xml.Name {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//从一个matter中获取其 []dav.Propstat
|
||||||
|
func (this *DavService) Propstats(matter *Matter, propfind dav.Propfind) []dav.Propstat {
|
||||||
|
|
||||||
|
propstats := make([]dav.Propstat, 0)
|
||||||
|
if propfind.Propname != nil {
|
||||||
|
this.PanicBadRequest("propfind.Propname != nil 尚未处理")
|
||||||
|
} else if propfind.Allprop != nil {
|
||||||
|
this.PanicBadRequest("propfind.Allprop != nil 尚未处理")
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var properties []dav.Property
|
||||||
|
|
||||||
|
for _, prop := range propfind.Prop {
|
||||||
|
//TODO: deadprops尚未考虑
|
||||||
|
|
||||||
|
// Otherwise, it must either be a live property or we don't know it.
|
||||||
|
if liveProp := LivePropMap[prop]; liveProp.findFn != nil && (liveProp.dir || !matter.Dir) {
|
||||||
|
innerXML := liveProp.findFn(matter)
|
||||||
|
|
||||||
|
properties = append(properties, dav.Property{
|
||||||
|
XMLName: prop,
|
||||||
|
InnerXML: []byte(innerXML),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
//TODO: 某一项请求的prop没有对应的结果
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(properties) == 0 {
|
||||||
|
this.PanicBadRequest("请求的属性项无法解析!")
|
||||||
|
}
|
||||||
|
|
||||||
|
okPropstat := dav.Propstat{Status: http.StatusOK, Props: properties}
|
||||||
|
|
||||||
|
propstats = append(propstats, okPropstat)
|
||||||
|
}
|
||||||
|
|
||||||
|
return propstats
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//处理 方法
|
//处理 方法
|
||||||
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, subPath string) {
|
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, subPath string) {
|
||||||
|
|
||||||
@ -84,19 +126,11 @@ func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http
|
|||||||
|
|
||||||
for _, matter := range matters {
|
for _, matter := range matters {
|
||||||
|
|
||||||
fmt.Printf("开始分析 %s\n", matter.Name)
|
fmt.Printf("开始分析 %s\n", matter.Path)
|
||||||
|
|
||||||
var propstats []dav.Propstat
|
propstats := this.Propstats(matter, propfind)
|
||||||
var props = make([]dav.Property, 0)
|
path := fmt.Sprintf("%s%s", WEBDAV_PREFFIX, matter.Path)
|
||||||
props = append(props, dav.Property{
|
response := this.makePropstatResponse(path, propstats)
|
||||||
XMLName: xml.Name{Space: "DAV:"},
|
|
||||||
})
|
|
||||||
propstats = append(propstats, dav.Propstat{
|
|
||||||
Props: props,
|
|
||||||
ResponseDescription: "有点问题",
|
|
||||||
})
|
|
||||||
|
|
||||||
response := this.makePropstatResponse("/eyeblue/ready/go", propstats)
|
|
||||||
|
|
||||||
err = multiStatusWriter.Write(response)
|
err = multiStatusWriter.Write(response)
|
||||||
this.PanicError(err)
|
this.PanicError(err)
|
||||||
|
@ -29,11 +29,19 @@ func (Matter) TableName() string {
|
|||||||
return TABLE_PREFIX + "matter"
|
return TABLE_PREFIX + "matter"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取该Matter的绝对路径。path代表的是相对路径。
|
// 获取该Matter的绝对路径。path代表的是相对路径。
|
||||||
func (this *Matter) AbsolutePath() string {
|
func (this *Matter) AbsolutePath() string {
|
||||||
return GetUserFileRootDir(this.Username) + this.Path
|
return GetUserFileRootDir(this.Username) + this.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取该Matter的MimeType
|
||||||
|
func (this *Matter) MimeType() string {
|
||||||
|
return GetMimeType(GetExtension(this.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//创建一个 ROOT 的matter,主要用于统一化处理移动复制等内容。
|
//创建一个 ROOT 的matter,主要用于统一化处理移动复制等内容。
|
||||||
func NewRootMatter(user *User) *Matter {
|
func NewRootMatter(user *User) *Matter {
|
||||||
matter := &Matter{}
|
matter := &Matter{}
|
||||||
|
Reference in New Issue
Block a user