|
|
|
|
|
|
|
|
|
package webdav |
|
|
|
|
|
|
|
|
|
import ( |
|
"strings" |
|
) |
|
|
|
|
|
type ifHeader struct { |
|
lists []ifList |
|
} |
|
|
|
|
|
type ifList struct { |
|
resourceTag string |
|
conditions []Condition |
|
} |
|
|
|
|
|
|
|
|
|
func parseIfHeader(httpHeader string) (h ifHeader, ok bool) { |
|
s := strings.TrimSpace(httpHeader) |
|
switch tokenType, _, _ := lex(s); tokenType { |
|
case '(': |
|
return parseNoTagLists(s) |
|
case angleTokenType: |
|
return parseTaggedLists(s) |
|
default: |
|
return ifHeader{}, false |
|
} |
|
} |
|
|
|
func parseNoTagLists(s string) (h ifHeader, ok bool) { |
|
for { |
|
l, remaining, ok := parseList(s) |
|
if !ok { |
|
return ifHeader{}, false |
|
} |
|
h.lists = append(h.lists, l) |
|
if remaining == "" { |
|
return h, true |
|
} |
|
s = remaining |
|
} |
|
} |
|
|
|
func parseTaggedLists(s string) (h ifHeader, ok bool) { |
|
resourceTag, n := "", 0 |
|
for first := true; ; first = false { |
|
tokenType, tokenStr, remaining := lex(s) |
|
switch tokenType { |
|
case angleTokenType: |
|
if !first && n == 0 { |
|
return ifHeader{}, false |
|
} |
|
resourceTag, n = tokenStr, 0 |
|
s = remaining |
|
case '(': |
|
n++ |
|
l, remaining, ok := parseList(s) |
|
if !ok { |
|
return ifHeader{}, false |
|
} |
|
l.resourceTag = resourceTag |
|
h.lists = append(h.lists, l) |
|
if remaining == "" { |
|
return h, true |
|
} |
|
s = remaining |
|
default: |
|
return ifHeader{}, false |
|
} |
|
} |
|
} |
|
|
|
func parseList(s string) (l ifList, remaining string, ok bool) { |
|
tokenType, _, s := lex(s) |
|
if tokenType != '(' { |
|
return ifList{}, "", false |
|
} |
|
for { |
|
tokenType, _, remaining = lex(s) |
|
if tokenType == ')' { |
|
if len(l.conditions) == 0 { |
|
return ifList{}, "", false |
|
} |
|
return l, remaining, true |
|
} |
|
c, remaining, ok := parseCondition(s) |
|
if !ok { |
|
return ifList{}, "", false |
|
} |
|
l.conditions = append(l.conditions, c) |
|
s = remaining |
|
} |
|
} |
|
|
|
func parseCondition(s string) (c Condition, remaining string, ok bool) { |
|
tokenType, tokenStr, s := lex(s) |
|
if tokenType == notTokenType { |
|
c.Not = true |
|
tokenType, tokenStr, s = lex(s) |
|
} |
|
switch tokenType { |
|
case strTokenType, angleTokenType: |
|
c.Token = tokenStr |
|
case squareTokenType: |
|
c.ETag = tokenStr |
|
default: |
|
return Condition{}, "", false |
|
} |
|
return c, s, true |
|
} |
|
|
|
|
|
|
|
const ( |
|
errTokenType = rune(-1) |
|
eofTokenType = rune(-2) |
|
strTokenType = rune(-3) |
|
notTokenType = rune(-4) |
|
angleTokenType = rune(-5) |
|
squareTokenType = rune(-6) |
|
) |
|
|
|
func lex(s string) (tokenType rune, tokenStr string, remaining string) { |
|
|
|
|
|
|
|
for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') { |
|
s = s[1:] |
|
} |
|
if len(s) == 0 { |
|
return eofTokenType, "", "" |
|
} |
|
i := 0 |
|
loop: |
|
for ; i < len(s); i++ { |
|
switch s[i] { |
|
case '\t', ' ', '(', ')', '<', '>', '[', ']': |
|
break loop |
|
} |
|
} |
|
|
|
if i != 0 { |
|
tokenStr, remaining = s[:i], s[i:] |
|
if tokenStr == "Not" { |
|
return notTokenType, "", remaining |
|
} |
|
return strTokenType, tokenStr, remaining |
|
} |
|
|
|
j := 0 |
|
switch s[0] { |
|
case '<': |
|
j, tokenType = strings.IndexByte(s, '>'), angleTokenType |
|
case '[': |
|
j, tokenType = strings.IndexByte(s, ']'), squareTokenType |
|
default: |
|
return rune(s[0]), "", s[1:] |
|
} |
|
if j < 0 { |
|
return errTokenType, "", "" |
|
} |
|
return tokenType, s[1:j], s[j+1:] |
|
} |
|
|