if.go 4.1 KB

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