github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/if.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  // Copyright 2014 The Go Authors. All rights reserved.
    20  // Use of this source code is governed by a BSD-style
    21  // license that can be found in the LICENSE file.
    22  
    23  package ocdav
    24  
    25  // copy of https://github.com/golang/net/blob/master/webdav/if.go
    26  
    27  // The If header is covered by Section 10.4.
    28  // http://www.webdav.org/specs/rfc4918.html#HEADER_If
    29  
    30  import (
    31  	"strings"
    32  )
    33  
    34  // ifHeader is a disjunction (OR) of ifLists.
    35  type ifHeader struct {
    36  	lists []ifList
    37  }
    38  
    39  // ifList is a conjunction (AND) of Conditions, and an optional resource tag.
    40  type ifList struct {
    41  	resourceTag string
    42  	conditions  []Condition
    43  }
    44  
    45  // parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string
    46  // should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is
    47  // returned by req.Header.Get("If") for a http.Request req.
    48  func parseIfHeader(httpHeader string) (h ifHeader, ok bool) {
    49  	s := strings.TrimSpace(httpHeader)
    50  	switch tokenType, _, _ := lex(s); tokenType {
    51  	case '(':
    52  		return parseNoTagLists(s)
    53  	case angleTokenType:
    54  		return parseTaggedLists(s)
    55  	default:
    56  		return ifHeader{}, false
    57  	}
    58  }
    59  
    60  func parseNoTagLists(s string) (h ifHeader, ok bool) {
    61  	for {
    62  		l, remaining, ok := parseList(s)
    63  		if !ok {
    64  			return ifHeader{}, false
    65  		}
    66  		h.lists = append(h.lists, l)
    67  		if remaining == "" {
    68  			return h, true
    69  		}
    70  		s = remaining
    71  	}
    72  }
    73  
    74  func parseTaggedLists(s string) (h ifHeader, ok bool) {
    75  	resourceTag, n := "", 0
    76  	for first := true; ; first = false {
    77  		tokenType, tokenStr, remaining := lex(s)
    78  		switch tokenType {
    79  		case angleTokenType:
    80  			if !first && n == 0 {
    81  				return ifHeader{}, false
    82  			}
    83  			resourceTag, n = tokenStr, 0
    84  			s = remaining
    85  		case '(':
    86  			n++
    87  			l, remaining, ok := parseList(s)
    88  			if !ok {
    89  				return ifHeader{}, false
    90  			}
    91  			l.resourceTag = resourceTag
    92  			h.lists = append(h.lists, l)
    93  			if remaining == "" {
    94  				return h, true
    95  			}
    96  			s = remaining
    97  		default:
    98  			return ifHeader{}, false
    99  		}
   100  	}
   101  }
   102  
   103  func parseList(s string) (l ifList, remaining string, ok bool) {
   104  	tokenType, _, s := lex(s)
   105  	if tokenType != '(' {
   106  		return ifList{}, "", false
   107  	}
   108  	for {
   109  		tokenType, _, remaining = lex(s)
   110  		if tokenType == ')' {
   111  			if len(l.conditions) == 0 {
   112  				return ifList{}, "", false
   113  			}
   114  			return l, remaining, true
   115  		}
   116  		c, remaining, ok := parseCondition(s)
   117  		if !ok {
   118  			return ifList{}, "", false
   119  		}
   120  		l.conditions = append(l.conditions, c)
   121  		s = remaining
   122  	}
   123  }
   124  
   125  func parseCondition(s string) (c Condition, remaining string, ok bool) {
   126  	tokenType, tokenStr, s := lex(s)
   127  	if tokenType == notTokenType {
   128  		c.Not = true
   129  		tokenType, tokenStr, s = lex(s)
   130  	}
   131  	switch tokenType {
   132  	case strTokenType, angleTokenType:
   133  		c.Token = tokenStr
   134  	case squareTokenType:
   135  		c.ETag = tokenStr
   136  	default:
   137  		return Condition{}, "", false
   138  	}
   139  	return c, s, true
   140  }
   141  
   142  // Single-rune tokens like '(' or ')' have a token type equal to their rune.
   143  // All other tokens have a negative token type.
   144  const (
   145  	errTokenType    = rune(-1)
   146  	eofTokenType    = rune(-2)
   147  	strTokenType    = rune(-3)
   148  	notTokenType    = rune(-4)
   149  	angleTokenType  = rune(-5)
   150  	squareTokenType = rune(-6)
   151  )
   152  
   153  func lex(s string) (tokenType rune, tokenStr string, remaining string) {
   154  	// The net/textproto Reader that parses the HTTP header will collapse
   155  	// Linear White Space that spans multiple "\r\n" lines to a single " ",
   156  	// so we don't need to look for '\r' or '\n'.
   157  	for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') {
   158  		s = s[1:]
   159  	}
   160  	if len(s) == 0 {
   161  		return eofTokenType, "", ""
   162  	}
   163  	i := 0
   164  loop:
   165  	for ; i < len(s); i++ {
   166  		switch s[i] {
   167  		case '\t', ' ', '(', ')', '<', '>', '[', ']':
   168  			break loop
   169  		}
   170  	}
   171  
   172  	if i != 0 {
   173  		tokenStr, remaining = s[:i], s[i:]
   174  		if tokenStr == "Not" {
   175  			return notTokenType, "", remaining
   176  		}
   177  		return strTokenType, tokenStr, remaining
   178  	}
   179  
   180  	j := 0
   181  	switch s[0] {
   182  	case '<':
   183  		j, tokenType = strings.IndexByte(s, '>'), angleTokenType
   184  	case '[':
   185  		j, tokenType = strings.IndexByte(s, ']'), squareTokenType
   186  	default:
   187  		return rune(s[0]), "", s[1:]
   188  	}
   189  	if j < 0 {
   190  		return errTokenType, "", ""
   191  	}
   192  	return tokenType, s[1:j], s[j+1:]
   193  }