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

     1  // Copyright 2018-2022 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  package net
    20  
    21  import (
    22  	"net/url"
    23  	"strings"
    24  
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  var (
    29  	// ErrInvalidHeaderValue defines an error which can occure when trying to parse a header value.
    30  	ErrInvalidHeaderValue = errors.New("invalid value")
    31  )
    32  
    33  type ctxKey int
    34  
    35  const (
    36  	// CtxKeyBaseURI is the key of the base URI context field
    37  	CtxKeyBaseURI ctxKey = iota
    38  
    39  	// NsDav is the Dav ns
    40  	NsDav = "DAV:"
    41  	// NsOwncloud is the owncloud ns
    42  	NsOwncloud = "http://owncloud.org/ns"
    43  	// NsOCS is the OCS ns
    44  	NsOCS = "http://open-collaboration-services.org/ns"
    45  
    46  	// RFC1123 time that mimics oc10. time.RFC1123 would end in "UTC", see https://github.com/golang/go/issues/13781
    47  	RFC1123 = "Mon, 02 Jan 2006 15:04:05 GMT"
    48  
    49  	// PropQuotaUnknown is the quota unknown property
    50  	PropQuotaUnknown = "-2"
    51  	// PropOcFavorite is the favorite ns property
    52  	PropOcFavorite = "http://owncloud.org/ns/favorite"
    53  	// PropOcMetaPathForUser is the meta-path-for-user ns property
    54  	PropOcMetaPathForUser = "http://owncloud.org/ns/meta-path-for-user"
    55  
    56  	// DepthZero represents the webdav zero depth value
    57  	DepthZero Depth = "0"
    58  	// DepthOne represents the webdav one depth value
    59  	DepthOne Depth = "1"
    60  	// DepthInfinity represents the webdav infinity depth value
    61  	DepthInfinity Depth = "infinity"
    62  )
    63  
    64  // Depth is a type representing the webdav depth header value
    65  type Depth string
    66  
    67  // String returns the string representation of the webdav depth value
    68  func (d Depth) String() string {
    69  	return string(d)
    70  }
    71  
    72  // EncodePath encodes the path of a url.
    73  //
    74  // slashes (/) are treated as path-separators.
    75  func EncodePath(path string) string {
    76  	return (&url.URL{Path: path}).EscapedPath()
    77  }
    78  
    79  // ParseDepth parses the depth header value defined in https://tools.ietf.org/html/rfc4918#section-9.1
    80  // Valid values are "0", "1" and "infinity". An empty string will be parsed to "1".
    81  // For all other values this method returns an error.
    82  func ParseDepth(s string) (Depth, error) {
    83  	if s == "" {
    84  		return DepthOne, nil
    85  	}
    86  
    87  	switch strings.ToLower(s) {
    88  	case DepthZero.String():
    89  		return DepthZero, nil
    90  	case DepthOne.String():
    91  		return DepthOne, nil
    92  	case DepthInfinity.String():
    93  		return DepthInfinity, nil
    94  	default:
    95  		return "", errors.Wrapf(ErrInvalidHeaderValue, "invalid depth: %s", s)
    96  	}
    97  }
    98  
    99  // ParseOverwrite parses the overwrite header value defined in https://datatracker.ietf.org/doc/html/rfc4918#section-10.6
   100  // Valid values are "T" and "F". An empty string will be parse to true.
   101  func ParseOverwrite(s string) (bool, error) {
   102  	if s == "" {
   103  		s = "T"
   104  	}
   105  	if s != "T" && s != "F" {
   106  		return false, errors.Wrapf(ErrInvalidHeaderValue, "invalid overwrite: %s", s)
   107  	}
   108  	return s == "T", nil
   109  }
   110  
   111  // ParseDestination parses the destination header value defined in https://datatracker.ietf.org/doc/html/rfc4918#section-10.3
   112  // The returned path will be relative to the given baseURI.
   113  func ParseDestination(baseURI, s string) (string, error) {
   114  	if s == "" {
   115  		return "", errors.Wrap(ErrInvalidHeaderValue, "destination header is empty")
   116  	}
   117  	dstURL, err := url.ParseRequestURI(s)
   118  	if err != nil {
   119  		return "", errors.Wrap(ErrInvalidHeaderValue, err.Error())
   120  	}
   121  
   122  	// TODO check if path is on same storage, return 502 on problems, see https://tools.ietf.org/html/rfc4918#section-9.9.4
   123  	// TODO make request.php optional in destination header
   124  	// Strip the base URI from the destination. The destination might contain redirection prefixes which need to be handled
   125  	urlSplit := strings.Split(dstURL.Path, baseURI)
   126  	if len(urlSplit) != 2 {
   127  		return "", errors.Wrap(ErrInvalidHeaderValue, "destination path does not contain base URI")
   128  	}
   129  
   130  	return urlSplit[1], nil
   131  }
   132  
   133  // ParsePrefer parses the prefer header value defined in https://datatracker.ietf.org/doc/html/rfc8144
   134  func ParsePrefer(s string) map[string]string {
   135  	parts := strings.Split(s, ",")
   136  	m := make(map[string]string, len(parts))
   137  	for _, part := range parts {
   138  		kv := strings.SplitN(strings.ToLower(strings.Trim(part, " ")), "=", 2)
   139  		if len(kv) == 2 {
   140  			m[kv[0]] = kv[1]
   141  		} else {
   142  			m[kv[0]] = "1" // mark it as set
   143  		}
   144  	}
   145  	return m
   146  }