Skip to content

Commit dd013ac

Browse files
committed
chore: add webdav package
1 parent 3934d90 commit dd013ac

23 files changed

+15357
-0
lines changed

server/webdav/file.go

+803
Large diffs are not rendered by default.

server/webdav/file_test.go

+1,183
Large diffs are not rendered by default.

server/webdav/if.go

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2014 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package webdav
6+
7+
// The If header is covered by Section 10.4.
8+
// http://www.webdav.org/specs/rfc4918.html#HEADER_If
9+
10+
import (
11+
"strings"
12+
)
13+
14+
// ifHeader is a disjunction (OR) of ifLists.
15+
type ifHeader struct {
16+
lists []ifList
17+
}
18+
19+
// ifList is a conjunction (AND) of Conditions, and an optional resource tag.
20+
type ifList struct {
21+
resourceTag string
22+
conditions []Condition
23+
}
24+
25+
// parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string
26+
// should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is
27+
// returned by req.Header.Get("If") for a http.Request req.
28+
func parseIfHeader(httpHeader string) (h ifHeader, ok bool) {
29+
s := strings.TrimSpace(httpHeader)
30+
switch tokenType, _, _ := lex(s); tokenType {
31+
case '(':
32+
return parseNoTagLists(s)
33+
case angleTokenType:
34+
return parseTaggedLists(s)
35+
default:
36+
return ifHeader{}, false
37+
}
38+
}
39+
40+
func parseNoTagLists(s string) (h ifHeader, ok bool) {
41+
for {
42+
l, remaining, ok := parseList(s)
43+
if !ok {
44+
return ifHeader{}, false
45+
}
46+
h.lists = append(h.lists, l)
47+
if remaining == "" {
48+
return h, true
49+
}
50+
s = remaining
51+
}
52+
}
53+
54+
func parseTaggedLists(s string) (h ifHeader, ok bool) {
55+
resourceTag, n := "", 0
56+
for first := true; ; first = false {
57+
tokenType, tokenStr, remaining := lex(s)
58+
switch tokenType {
59+
case angleTokenType:
60+
if !first && n == 0 {
61+
return ifHeader{}, false
62+
}
63+
resourceTag, n = tokenStr, 0
64+
s = remaining
65+
case '(':
66+
n++
67+
l, remaining, ok := parseList(s)
68+
if !ok {
69+
return ifHeader{}, false
70+
}
71+
l.resourceTag = resourceTag
72+
h.lists = append(h.lists, l)
73+
if remaining == "" {
74+
return h, true
75+
}
76+
s = remaining
77+
default:
78+
return ifHeader{}, false
79+
}
80+
}
81+
}
82+
83+
func parseList(s string) (l ifList, remaining string, ok bool) {
84+
tokenType, _, s := lex(s)
85+
if tokenType != '(' {
86+
return ifList{}, "", false
87+
}
88+
for {
89+
tokenType, _, remaining = lex(s)
90+
if tokenType == ')' {
91+
if len(l.conditions) == 0 {
92+
return ifList{}, "", false
93+
}
94+
return l, remaining, true
95+
}
96+
c, remaining, ok := parseCondition(s)
97+
if !ok {
98+
return ifList{}, "", false
99+
}
100+
l.conditions = append(l.conditions, c)
101+
s = remaining
102+
}
103+
}
104+
105+
func parseCondition(s string) (c Condition, remaining string, ok bool) {
106+
tokenType, tokenStr, s := lex(s)
107+
if tokenType == notTokenType {
108+
c.Not = true
109+
tokenType, tokenStr, s = lex(s)
110+
}
111+
switch tokenType {
112+
case strTokenType, angleTokenType:
113+
c.Token = tokenStr
114+
case squareTokenType:
115+
c.ETag = tokenStr
116+
default:
117+
return Condition{}, "", false
118+
}
119+
return c, s, true
120+
}
121+
122+
// Single-rune tokens like '(' or ')' have a token type equal to their rune.
123+
// All other tokens have a negative token type.
124+
const (
125+
errTokenType = rune(-1)
126+
eofTokenType = rune(-2)
127+
strTokenType = rune(-3)
128+
notTokenType = rune(-4)
129+
angleTokenType = rune(-5)
130+
squareTokenType = rune(-6)
131+
)
132+
133+
func lex(s string) (tokenType rune, tokenStr string, remaining string) {
134+
// The net/textproto Reader that parses the HTTP header will collapse
135+
// Linear White Space that spans multiple "\r\n" lines to a single " ",
136+
// so we don't need to look for '\r' or '\n'.
137+
for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') {
138+
s = s[1:]
139+
}
140+
if len(s) == 0 {
141+
return eofTokenType, "", ""
142+
}
143+
i := 0
144+
loop:
145+
for ; i < len(s); i++ {
146+
switch s[i] {
147+
case '\t', ' ', '(', ')', '<', '>', '[', ']':
148+
break loop
149+
}
150+
}
151+
152+
if i != 0 {
153+
tokenStr, remaining = s[:i], s[i:]
154+
if tokenStr == "Not" {
155+
return notTokenType, "", remaining
156+
}
157+
return strTokenType, tokenStr, remaining
158+
}
159+
160+
j := 0
161+
switch s[0] {
162+
case '<':
163+
j, tokenType = strings.IndexByte(s, '>'), angleTokenType
164+
case '[':
165+
j, tokenType = strings.IndexByte(s, ']'), squareTokenType
166+
default:
167+
return rune(s[0]), "", s[1:]
168+
}
169+
if j < 0 {
170+
return errTokenType, "", ""
171+
}
172+
return tokenType, s[1:j], s[j+1:]
173+
}

0 commit comments

Comments
 (0)