Class: Net::DAV

Inherits:
Object
  • Object
show all
Defined in:
lib/net/dav.rb,
lib/net/dav/item.rb

Overview

Implement a WebDAV client

Defined Under Namespace

Classes: CurlHandler, Item, NetHttpHandler

Constant Summary

MAX_REDIRECTS =
10

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri, options = nil) ⇒ DAV

Creates a new Net::DAV object for the specified host The path part of the URI is used to handle relative URLs in subsequent requests. You can pass :curl => false if you want to disable use of the curb (libcurl) gem if present for acceleration



408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/net/dav.rb', line 408

def initialize(uri, options = nil)
  @last_status = 0

  @have_curl = Curl rescue nil
  if options && options.has_key?(:curl) && !options[:curl]
    @have_curl = false
  end
  @uri = uri
  @uri = URI.parse(@uri) if @uri.is_a? String
  @handler = @have_curl ? CurlHandler.new(@uri) : NetHttpHandler.new(@uri)
  @headers = options && options[:headers] ? options[:headers] : {}
end

Class Method Details

.start(uri, options = nil, &block) ⇒ Object

Creates a new Net::DAV object and opens the connection to the host. Yields the object to the block.

Example:

res = Net::DAV.start(url) do |dav|
  dav.find(url.path) do |item|
    puts "#{item.uri} is size #{item.size}"
  end
end


399
400
401
# File 'lib/net/dav.rb', line 399

def self.start(uri, options = nil, &block) # :yield: dav
  new(uri, options).start(&block)
end

Instance Method Details

#cd(url) ⇒ Object

Change the base URL for use in handling relative paths



581
582
583
584
585
586
587
# File 'lib/net/dav.rb', line 581

def cd(url)
  new_uri = @uri.merge(url)
  if new_uri.host != @uri.host || new_uri.port != @uri.port || new_uri.scheme != @uri.scheme
    raise Exception , "uri must have same scheme, host and port"
  end
  @uri = new_uri
end

#copy(path, destination) ⇒ Object

Send a copy request to the server.

Example:

dav.copy(original_path, destination)


651
652
653
654
655
656
657
# File 'lib/net/dav.rb', line 651

def copy(path,destination)
  path = @uri.merge(path).path
  destination = @uri.merge(destination).to_s
  headers = {'Destination' => destination}
  res = @handler.request(:copy, path, nil, headers.merge(@headers))
  res.body
end

#credentials(user, pass) ⇒ Object

Set credentials for basic authentication



437
438
439
440
441
442
443
444
# File 'lib/net/dav.rb', line 437

def credentials(user, pass)
  @handler.user = user
  @handler.pass = pass

  # Return something explicitly since this command might be run in a
  # console where the last statement would be printed.
  nil
end

#delete(path) ⇒ Object

Delete request

Example:

dav.delete(uri.path)


629
630
631
632
633
# File 'lib/net/dav.rb', line 629

def delete(path)
  path = @uri.merge(path).path
  res = @handler.request(:delete, path, nil, @headers)
  res.body
end

#disable_basic_auth=(value) ⇒ Object



361
362
363
# File 'lib/net/dav.rb', line 361

def disable_basic_auth=(value)
  @handler.disable_basic_auth = value
end

#disable_basic_auth?Boolean

Disable basic auth - to protect passwords from going in the clear through a man-in-the-middle attack.



357
358
359
# File 'lib/net/dav.rb', line 357

def disable_basic_auth?
  @handler.disable_basic_auth
end

#exists?(path) ⇒ Boolean

Returns true if resource exists on server.

Example:

dav.exists?('https://www.example.com/collection/')  => true
dav.exists?('/collection/')  => true


711
712
713
714
715
716
717
718
719
720
721
# File 'lib/net/dav.rb', line 711

def exists?(path)
  path = @uri.merge(path).path
  headers = {'Depth' => '1'}
  body = '<?xml version="1.0" encoding="utf-8"?><DAV:propfind xmlns:DAV="DAV:"><DAV:allprop/></DAV:propfind>'
  begin
    res = @handler.request(:propfind, path, body, headers.merge(@headers))
  rescue
    return false
  end
  return (res.is_a? Net::HTTPSuccess)
end

#find(path, options = {}) ⇒ Object

Find files and directories, yields Net::DAV::Item

The :filename option can be a regexp or string, and is used to filter the yielded items.

If :suppress_errors is passed, exceptions that occurs when reading directory information is ignored, and a warning is printed out stderr instead.

The default is to not traverse recursively, unless the :recursive options is passed.

Examples:

res = Net::DAV.start(url) do |dav|
  dav.find(url.path, :recursive => true) do |item|
    puts "#{item.type} #{item.uri}"
    puts item.content
  end
end

dav = Net::DAV.new(url)
dav.find(url.path, :filename => /\.html/, :suppress_errors => true)
  puts item.url.to_s
end


525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/net/dav.rb', line 525

def find(path, options = {})
  path = @uri.merge(path).path
  namespaces = {'x' => "DAV:"}
  begin
    doc = propfind(path)
  rescue Net::ProtocolError => e
    msg = e.to_s + ": " + path.to_s
    if(options[:suppress_errors])then
      # Ignore dir if propfind returns an error
      warn("Warning: " + msg)
      return nil
    else
      raise e.class.new(msg, nil)
    end
  end
  path.sub!(/\/$/, '')
  doc./('.//x:response', namespaces).each do |item|
    uri = @uri.merge(item.xpath("x:href", namespaces).inner_text)
    size = item.%(".//x:getcontentlength", namespaces).inner_text rescue nil
    type = item.%(".//x:collection", namespaces) ? :directory : :file
    res = Item.new(self, uri, type, size, item)
    if type == :file then

      if(options[:filename])then
        search_term = options[:filename]
        filename = File.basename(uri.path)
        if(search_term.class == Regexp and search_term.match(filename))then
          yield res
        elsif(search_term.class == String and search_term == filename)then
          yield res
        end
      else
        yield res
      end

    elsif uri.path == path || uri.path == path + "/"
      # This is the top-level dir, skip it
    elsif options[:recursive] && type == :directory

      if(!options[:filename])then
        yield res
      end

      # This is a subdir, recurse
      find(uri.path, options) do |sub_res|
        yield sub_res
      end
    else
      if(!options[:filename])then
        yield res
      end
    end
  end
end

#get(path, &block) ⇒ Object

Get the content of a resource as a string

If called with a block, yields each fragment of the entity body in turn as a string as it is read from the socket. Note that in this case, the returned response object will not contain a (meaningful) body.



596
597
598
599
600
# File 'lib/net/dav.rb', line 596

def get(path, &block)
  path = @uri.merge(path).path
  body = @handler.request_returning_body(:get, path, @headers, &block)
  body
end

#headers(headers) ⇒ Object

Set extra headers for the dav request



463
464
465
# File 'lib/net/dav.rb', line 463

def headers(headers)
  @headers = headers
end

#last_statusObject



17
18
19
# File 'lib/net/dav.rb', line 17

def last_status
  @handler.last_status
end

#lock(path, xml_snippet) ⇒ Object

Send a lock request to the server

On success returns an XML response body with a Lock-Token

Example:

dav.lock(uri.path, "<d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:owner>Owner</d:owner>")


685
686
687
688
689
690
691
692
693
694
# File 'lib/net/dav.rb', line 685

def lock(path, xml_snippet)
  path = @uri.merge(path).path
  headers = {'Depth' => '1'}
  body =  '<?xml version="1.0"?>' +
  '<d:lockinfo xmlns:d="DAV:">' +
     xml_snippet +
  '</d:lockinfo>'
  res = @handler.request(:lock, path, body, headers.merge(@headers))
  Nokogiri::XML.parse(res.body)
end

#mkdir(path) ⇒ Object

Makes a new directory (collection)



724
725
726
727
728
# File 'lib/net/dav.rb', line 724

def mkdir(path)
  path = @uri.merge(path).path
  res = @handler.request(:mkcol, path, nil, @headers)
  res.body
end

#move(path, destination) ⇒ Object

Send a move request to the server.

Example:

dav.move(original_path, new_path)


639
640
641
642
643
644
645
# File 'lib/net/dav.rb', line 639

def move(path,destination)
  path = @uri.merge(path).path
  destination = @uri.merge(destination).to_s
  headers = {'Destination' => destination}
  res = @handler.request(:move, path, nil, headers.merge(@headers))
  res.body
end

#open_timeoutObject

Seconds to wait until connection is opened. If the DAV object cannot open a connection in this many seconds, it raises a TimeoutError exception.



381
382
383
# File 'lib/net/dav.rb', line 381

def open_timeout
  @handler.read_timeout
end

#open_timeout=(sec) ⇒ Object



385
386
387
# File 'lib/net/dav.rb', line 385

def open_timeout=(sec)
  @handler.read_timeout = sec
end

#propfind(path, *options) ⇒ Object

Perform a PROPFIND request

Example:

Basic propfind:

properties = propfind('/path/')

Get ACL for resource:

properties = propfind('/path/', :acl)

Custom propfind:

properties = propfind('/path/', '<?xml version="1.0" encoding="utf-8"?>...')

See webdav.org/specs/rfc3744.html#rfc.section.5.9 for more on how to retrieve access control properties.



485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/net/dav.rb', line 485

def propfind(path,*options)
  headers = {'Depth' => '1'}
  if(options[0] == :acl)
    body = '<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><D:owner/>' +
            '<D:supported-privilege-set/><D:current-user-privilege-set/><D:acl/></D:prop></D:propfind>'
  else
    body = options[0]
  end
  if(!body)
    body = '<?xml version="1.0" encoding="utf-8"?><DAV:propfind xmlns:DAV="DAV:"><DAV:allprop/></DAV:propfind>'
  end
  res = @handler.request(:propfind, path, body, headers.merge(@headers))
  Nokogiri::XML.parse(res.body)
end

#proppatch(path, xml_snippet) ⇒ Object

Do a proppatch request to the server to update properties on resources or collections.

Example:

dav.proppatch(uri.path,
  "<d:set><d:prop>" +
  "<d:creationdate>#{new_date}</d:creationdate>" +
  "</d:set></d:prop>" +
  )


668
669
670
671
672
673
674
675
676
677
# File 'lib/net/dav.rb', line 668

def proppatch(path, xml_snippet)
  path = @uri.merge(path).path
  headers = {'Depth' => '1'}
  body =  '<?xml version="1.0"?>' +
  '<d:propertyupdate xmlns:d="DAV:">' +
     xml_snippet +
  '</d:propertyupdate>'
  res = @handler.request(:proppatch, path, body, headers.merge(@headers))
  Nokogiri::XML.parse(res.body)
end

#put(path, stream, length) ⇒ Object

Stores the content of a stream to a URL

Example: File.open(file, “r”) do |stream|

dav.put(url.path, stream, File.size(file))

end



608
609
610
611
612
# File 'lib/net/dav.rb', line 608

def put(path, stream, length)
  path = @uri.merge(path).path
  res = @handler.request_sending_stream(:put, path, stream, length, @headers)
  res.body
end

#put_string(path, str) ⇒ Object

Stores the content of a string to a URL

Example:

dav.put(url.path, "hello world")


619
620
621
622
623
# File 'lib/net/dav.rb', line 619

def put_string(path, str)
  path = @uri.merge(path).path
  res = @handler.request_sending_body(:put, path, str, @headers)
  res.body
end

#read_timeoutObject

Seconds to wait until reading one block (by one system call). If the DAV object cannot read a block in this many seconds, it raises a TimeoutError exception.



369
370
371
# File 'lib/net/dav.rb', line 369

def read_timeout
  @handler.read_timeout
end

#read_timeout=(sec) ⇒ Object



373
374
375
# File 'lib/net/dav.rb', line 373

def read_timeout=(sec)
  @handler.read_timeout = sec
end

#ssl_authority(ca_file) ⇒ Object

Set additional ssl authorities for ssl certificate authentication



457
458
459
460
# File 'lib/net/dav.rb', line 457

def ssl_authority(ca_file)
  @handler.ca_file(ca_file)
  nil
end

#ssl_certificate(cert_file, *cert_file_password) ⇒ Object

Set credentials for ssl certificate authentication



447
448
449
450
451
452
453
454
# File 'lib/net/dav.rb', line 447

def ssl_certificate(cert_file, *cert_file_password)
  @handler.cert_file(cert_file)
  @handler.cert_key(cert_file, cert_file_password)

  # Return something explicitly since this command might be run in a
  # console where the last statement would be printed.
  nil
end

#start(&block) ⇒ Object

Opens the connection to the host. Yields self to the block.

Example:

res = Net::DAV.new(url).start do |dav|
  dav.find(url.path) do |item|
    puts item.inspect
  end
end


430
431
432
433
434
# File 'lib/net/dav.rb', line 430

def start(&block) # :yield: dav
  @handler.start do
    return yield(self)
  end
end

#unlock(path, locktoken) ⇒ Object

Send an unlock request to the server

Example:

dav.unlock(uri.path, "opaquelocktoken:eee47ade-09ac-626b-02f7-e354175d984e")


700
701
702
703
704
# File 'lib/net/dav.rb', line 700

def unlock(path, locktoken)
 headers = {'Lock-Token' => '<'+locktoken+'>'}
 path = @uri.merge(path).path
 res = @handler.request(:unlock, path, nil, headers.merge(@headers))
end

#verify_callback=(callback) ⇒ Object



730
731
732
# File 'lib/net/dav.rb', line 730

def verify_callback=(callback)
  @handler.verify_callback = callback
end

#verify_server=(value) ⇒ Object



734
735
736
# File 'lib/net/dav.rb', line 734

def verify_server=(value)
  @handler.verify_server = value
end