github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/webdav/if.go (about)

     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 Data 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  }