Class: Tilia::Dav::Client
- Defined in:
- lib/tilia/dav/client.rb
Overview
SabreDAV DAV client
This client wraps around Curl to provide a convenient API to a WebDAV server.
NOTE: This class is experimental, it’s api will likely change in the future.
Constant Summary collapse
- AUTH_BASIC =
Basic authentication
1
- AUTH_DIGEST =
Digest authentication
2
- AUTH_NTLM =
NTLM authentication
4
- ENCODING_IDENTITY =
Identity encoding, which basically does not nothing.
1
- ENCODING_DEFLATE =
Deflate encoding
2
- ENCODING_GZIP =
Gzip encoding
4
- ENCODING_ALL =
Sends all encoding headers.
7
Instance Attribute Summary collapse
- #property_map ⇒ Object deprecated Deprecated.
-
#xml ⇒ Object
The xml service.
Instance Method Summary collapse
-
#absolute_url(url) ⇒ Object
Returns the full url based on the given url (which may be relative).
-
#initialize(settings) ⇒ Client
constructor
Constructor.
-
#options ⇒ Object
Performs an HTTP options request.
-
#parse_multi_status(body) ⇒ Object
Parses a WebDAV multistatus response body.
-
#prop_find(url, properties, depth = 0) ⇒ Object
Does a PROPFIND request.
-
#prop_patch(url, properties) ⇒ Object
Updates a list of properties on the server.
-
#request(method, url = '', body = nil, headers = {}) ⇒ Object
Performs an actual HTTP request, and returns the result.
Constructor Details
#initialize(settings) ⇒ Client
Constructor
Settings are provided through the ‘settings’ argument. The following settings are supported:
* baseUri
* userName (optional)
* password (optional)
* proxy (optional)
* authType (optional)
* encoding (optional)
authType must be a bitmap, using AUTH_BASIC, AUTH_DIGEST
and AUTH_NTLM. If you know which authentication method will be
used, it's recommended to set it, as it will save a great deal of
requests to 'discover' this information.
Encoding is a bitmap with one of the ENCODING constants.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/tilia/dav/client.rb', line 79 def initialize(settings) @property_map = {} @encoding = ENCODING_IDENTITY unless settings.key?('baseUri') fail ArgumentError, 'A baseUri must be provided' end super() @base_uri = settings['baseUri'] add_curl_setting(:proxy, settings['proxy']) if settings.key?('proxy') if settings.key?('userName') user_name = settings['userName'] password = settings['password'] || '' if settings.key?('authType') curl_type = [] curl_type << :basic if settings['authType'] & AUTH_BASIC > 0 curl_type << :digest if settings['authType'] & AUTH_DIGEST > 0 curl_type << :ntlm if settings['authType'] & AUTH_NTLM > 0 else curl_type = [:basic, :digest] end add_curl_setting(:httpauth, curl_type) add_curl_setting(:userpwd, "#{user_name}:#{password}") end if settings.key?('encoding') encoding = settings['encoding'] encodings = [] encodings << 'identity' if encoding & ENCODING_IDENTITY > 0 encodings << 'deflate' if encoding & ENCODING_DEFLATE > 0 encodings << 'gzip' if encoding & ENCODING_GZIP > 0 add_curl_setting(:encoding, encodings.join(',')) end add_curl_setting(:useragent, "tilia-dav/#{Version::VERSION} (http://sabre.io/)") @xml = Xml::Service.new # BC @property_map = @xml.element_map end |
Instance Attribute Details
#property_map ⇒ Object
The elementMap
This property is linked via reference to @xml.element_map. It’s deprecated as of version 3.0.0, and should no longer be used.
24 25 26 |
# File 'lib/tilia/dav/client.rb', line 24 def property_map @property_map end |
#xml ⇒ Object
The xml service.
Uset this service to configure the property and namespace maps.
15 16 17 |
# File 'lib/tilia/dav/client.rb', line 15 def xml @xml end |
Instance Method Details
#absolute_url(url) ⇒ Object
Returns the full url based on the given url (which may be relative). All urls are expanded based on the base url as given by the server.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/tilia/dav/client.rb', line 316 def absolute_url(url) # If the url starts with http:// or https://, the url is already absolute. return url if url =~ /^http(s?):\/\// # If the url starts with a slash, we must calculate the url based off # the root of the base url. if url.index('/') == 0 parts = Tilia::Uri.parse(@base_uri) return "#{parts['scheme']}://#{parts['host']}#{parts['port'] ? ":#{parts['port']}" : ''}#{url}" end # Otherwise... @base_uri + url end |
#options ⇒ Object
Performs an HTTP options request
This method returns all the features from the ‘DAV:’ header as an array. If there was no DAV header, or no contents this method will return an empty array.
259 260 261 262 263 264 265 266 267 268 |
# File 'lib/tilia/dav/client.rb', line 259 def request = Http::Request.new('OPTIONS', absolute_url('')) response = send_request(request) dav = response.header('Dav') return [] unless dav features = dav.split(',') features.map(&:strip) end |
#parse_multi_status(body) ⇒ Object
Parses a WebDAV multistatus response body
This method returns an array with the following structure
[
'url/to/resource' => [
'200' => [
'{DAV:}property1' => 'value1',
'{DAV:}property2' => 'value2',
],
'404' => [
'{DAV:}property1' => null,
'{DAV:}property2' => null,
],
],
'url/to/resource2' => [
.. etc ..
]
]
354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/tilia/dav/client.rb', line 354 def parse_multi_status(body) multistatus = @xml.expect('{DAV:}multistatus', body) result = {} multistatus.responses.each do |response| result[response.href] = response.response_properties end result end |
#prop_find(url, properties, depth = 0) ⇒ Object
Does a PROPFIND request
The list of requested properties must be specified as an array, in clark notation.
The returned array will contain a list of filenames as keys, and properties as values.
The properties array will contain the list of properties. Only properties that are actually returned from the server (without error) will be returned, anything else is discarded.
Depth should be either 0 or 1. A depth of 1 will cause a request to be made to the server to also return all child resources.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/tilia/dav/client.rb', line 147 def prop_find(url, properties, depth = 0) dom = LibXML::XML::Document.new root = LibXML::XML::Node.new('d:propfind') LibXML::XML::Namespace.new(root, 'd', 'DAV:') prop = LibXML::XML::Node.new('d:prop') properties.each do |property| (namespace, element_name) = Tilia::Xml::Service.parse_clark_notation(property) if namespace == 'DAV:' element = LibXML::XML::Node.new("d:#{element_name}") else element = LibXML::XML::Node.new("x:#{element_name}") LibXML::XML::Namespace.new(element, 'x', namespace) end prop << element end dom.root = root root << prop body = dom.to_s url = absolute_url(url) request = Http::Request.new( 'PROPFIND', url, { 'Depth' => depth, 'Content-Type' => 'application/xml' }, body ) response = send_request(request) fail Http::ClientHttpException.new(response) if response.status.to_i >= 400 result = parse_multi_status(response.body_as_string) # If depth was 0, we only return the top item if depth == 0 result = result.first.second # value of first key/value pair return result.key?('200') ? result['200'] : {} end new_result = {} result.each do |href, status_list| new_result[href] = status_list.key?('200') ? status_list['200'] : {} end new_result end |
#prop_patch(url, properties) ⇒ Object
Updates a list of properties on the server
The list of properties must have clark-notation properties for the keys, and the actual (string) value for the value. If the value is null, an attempt is made to delete the property.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/tilia/dav/client.rb', line 213 def prop_patch(url, properties) prop_patch = Xml::Request::PropPatch.new prop_patch.properties = properties xml = @xml.write('{DAV:}propertyupdate', prop_patch) url = absolute_url(url) request = Http::Request.new( 'PROPPATCH', url, { 'Content-Type' => 'application/xml' }, xml ) response = send_request(request) fail Http::ClientHttpException.new(response) if response.status.to_i >= 400 if response.status == 207.to_i # If it's a 207, the request could still have failed, but the # information is hidden in the response body. result = parse_multi_status(response.body_as_string) error_properties = [] result.each do |href, status_list| status_list.each do |status, properties| next unless status.to_i >= 400 properties.each do |prop_name, prop_value| error_properties << "#{prop_name} (#{status})" end end end fail Http::ClientException, "ROPPATCH failed. The following properties errored: #{error_properties.join(', ')}" if error_properties.any? end true end |
#request(method, url = '', body = nil, headers = {}) ⇒ Object
Performs an actual HTTP request, and returns the result.
If the specified url is relative, it will be expanded based on the base url.
The returned array contains 3 keys:
* body - the response body
* httpCode - a HTTP code (200, 404, etc)
* headers - a list of response http headers. The header names have
been lowercased.
For large uploads, it’s highly recommended to specify body as a stream resource. You can easily do this by simply passing the result of fopen(…, ‘r’).
This method will throw an exception if an HTTP error was received. Any HTTP status code above 399 is considered an error.
Note that it is no longer recommended to use this method, use the send method instead.
297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/tilia/dav/client.rb', line 297 def request(method, url = '', body = nil, headers = {}) url = absolute_url(url) headers = {} response.headers.each { |k, v| headers[k.downcase] = v } response = send_request(Http::Request.new(method, url, headers, body)) { 'body' => response.body_as_string, 'statusCode' => response.status.to_i, 'headers' => headers } end |