github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/prop/prop.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 package prop 20 21 import ( 22 "bytes" 23 "encoding/xml" 24 "unicode/utf8" 25 ) 26 27 // PropertyXML represents a single DAV resource property as defined in RFC 4918. 28 // http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties 29 type PropertyXML struct { 30 // XMLName is the fully qualified name that identifies this property. 31 XMLName xml.Name 32 33 // Lang is an optional xml:lang attribute. 34 Lang string `xml:"xml:lang,attr,omitempty"` 35 36 // InnerXML contains the XML representation of the property value. 37 // See http://www.webdav.org/specs/rfc4918.html#property_values 38 // 39 // Property values of complex type or mixed-content must have fully 40 // expanded XML namespaces or be self-contained with according 41 // XML namespace declarations. They must not rely on any XML 42 // namespace declarations within the scope of the XML document, 43 // even including the DAV: namespace. 44 InnerXML []byte `xml:",innerxml"` 45 } 46 47 func xmlEscaped(val string) []byte { 48 buf := new(bytes.Buffer) 49 xml.Escape(buf, []byte(val)) 50 return buf.Bytes() 51 } 52 53 // EscapedNS returns a new PropertyXML instance while xml-escaping the value 54 func EscapedNS(namespace string, local string, val string) PropertyXML { 55 return PropertyXML{ 56 XMLName: xml.Name{Space: namespace, Local: local}, 57 Lang: "", 58 InnerXML: xmlEscaped(val), 59 } 60 } 61 62 var ( 63 escAmp = []byte("&") 64 escLT = []byte("<") 65 escGT = []byte(">") 66 escFFFD = []byte(string(utf8.RuneError)) // Unicode replacement character 67 ) 68 69 // Decide whether the given rune is in the XML Character Range, per 70 // the Char production of https://www.xml.com/axml/testaxml.htm, 71 // Section 2.2 Characters. 72 func isInCharacterRange(r rune) (inrange bool) { 73 return r == 0x09 || 74 r == 0x0A || 75 r == 0x0D || 76 r >= 0x20 && r <= 0xD7FF || 77 r >= 0xE000 && r <= utf8.RuneError || 78 r >= 0x10000 && r <= 0x10FFFF 79 } 80 81 // Escaped returns a new PropertyXML instance while replacing only 82 // * `&` with `&` 83 // * `<` with `<` 84 // * `>` with `>` 85 // as defined in https://www.w3.org/TR/REC-xml/#syntax: 86 // 87 // > The ampersand character (&) and the left angle bracket (<) must not appear 88 // > in their literal form, except when used as markup delimiters, or within a 89 // > comment, a processing instruction, or a CDATA section. If they are needed 90 // > elsewhere, they must be escaped using either numeric character references 91 // > or the strings " & " and " < " respectively. The right angle 92 // > bracket (>) may be represented using the string " > ", and must, for 93 // > compatibility, be escaped using either " > " or a character reference 94 // > when it appears in the string " ]]> " in content, when that string is not 95 // > marking the end of a CDATA section. 96 // 97 // The code ignores errors as the legacy Escaped() does 98 // TODO properly use the space 99 func Escaped(key, val string) PropertyXML { 100 s := []byte(val) 101 w := bytes.NewBuffer(make([]byte, 0, len(s))) 102 var esc []byte 103 last := 0 104 for i := 0; i < len(s); { 105 r, width := utf8.DecodeRune(s[i:]) 106 i += width 107 switch r { 108 case '&': 109 esc = escAmp 110 case '<': 111 esc = escLT 112 case '>': 113 esc = escGT 114 default: 115 if !isInCharacterRange(r) || (r == utf8.RuneError && width == 1) { 116 esc = escFFFD 117 break 118 } 119 continue 120 } 121 if _, err := w.Write(s[last : i-width]); err != nil { 122 break 123 } 124 if _, err := w.Write(esc); err != nil { 125 break 126 } 127 last = i 128 } 129 _, _ = w.Write(s[last:]) 130 return PropertyXML{ 131 XMLName: xml.Name{Space: "", Local: key}, 132 Lang: "", 133 InnerXML: w.Bytes(), 134 } 135 } 136 137 // NotFound returns a new PropertyXML instance with an empty value 138 func NotFound(key string) PropertyXML { 139 return PropertyXML{ 140 XMLName: xml.Name{Space: "", Local: key}, 141 Lang: "", 142 } 143 } 144 145 // NotFoundNS returns a new PropertyXML instance with the given namespace and an empty value 146 func NotFoundNS(namespace, key string) PropertyXML { 147 return PropertyXML{ 148 XMLName: xml.Name{Space: namespace, Local: key}, 149 Lang: "", 150 } 151 } 152 153 // Raw returns a new PropertyXML instance for the given key/value pair 154 // TODO properly use the space 155 func Raw(key, val string) PropertyXML { 156 return PropertyXML{ 157 XMLName: xml.Name{Space: "", Local: key}, 158 Lang: "", 159 InnerXML: []byte(val), 160 } 161 } 162 163 // Next returns the next token, if any, in the XML stream of d. 164 // RFC 4918 requires to ignore comments, processing instructions 165 // and directives. 166 // http://www.webdav.org/specs/rfc4918.html#property_values 167 // http://www.webdav.org/specs/rfc4918.html#xml-extensibility 168 func Next(d *xml.Decoder) (xml.Token, error) { 169 for { 170 t, err := d.Token() 171 if err != nil { 172 return t, err 173 } 174 switch t.(type) { 175 case xml.Comment, xml.Directive, xml.ProcInst: 176 continue 177 default: 178 return t, nil 179 } 180 } 181 } 182 183 // ActiveLock holds active lock xml data 184 // 185 // http://www.webdav.org/specs/rfc4918.html#ELEMENT_activelock 186 // 187 // <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?, 188 // 189 // locktoken?, lockroot)> 190 type ActiveLock struct { 191 XMLName xml.Name `xml:"activelock"` 192 Exclusive *struct{} `xml:"lockscope>exclusive,omitempty"` 193 Shared *struct{} `xml:"lockscope>shared,omitempty"` 194 Write *struct{} `xml:"locktype>write,omitempty"` 195 Depth string `xml:"depth"` 196 Owner Owner `xml:"owner,omitempty"` 197 Timeout string `xml:"timeout,omitempty"` 198 Locktoken string `xml:"locktoken>href"` 199 Lockroot string `xml:"lockroot>href,omitempty"` 200 } 201 202 // Owner captures the inner UML of a lock owner element http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner 203 type Owner struct { 204 InnerXML string `xml:",innerxml"` 205 } 206 207 // Escape repaces ", &, ', < and > with their xml representation 208 func Escape(s string) string { 209 b := bytes.NewBuffer(nil) 210 _ = xml.EscapeText(b, []byte(s)) 211 return b.String() 212 }