You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

174 lines
3.9 KiB

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