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 5package webdav 6 7// The If header is covered by Section 10.4. 8// http://www.webdav.org/specs/rfc4918.html#HEADER_If 9 10import ( 11 "strings" 12) 13 14// ifHeader is a disjunction (OR) of ifLists. 15type ifHeader struct { 16 lists []ifList 17} 18 19// ifList is a conjunction (AND) of Conditions, and an optional resource tag. 20type 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. 28func 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 40func 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 54func 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 83func 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 105func 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. 124const ( 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 133func 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 144loop: 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} 174