Class: Net::DAV

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

Overview

Implement a WebDAV client

Defined Under Namespace

Classes: CurlHandler, Item, NetHttpHandler

Constant Summary collapse

MAX_REDIRECTS =
10
VERSION =
"0.5.1"

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



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

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


401
402
403
# File 'lib/net/dav.rb', line 401

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



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

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)


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

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



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

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)


631
632
633
634
635
# File 'lib/net/dav.rb', line 631

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

#disable_basic_auth=(value) ⇒ Object



363
364
365
# File 'lib/net/dav.rb', line 363

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.

Returns:

  • (Boolean)


359
360
361
# File 'lib/net/dav.rb', line 359

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

Returns:

  • (Boolean)


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

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


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
579
580
# File 'lib/net/dav.rb', line 527

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.



598
599
600
601
602
# File 'lib/net/dav.rb', line 598

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



465
466
467
# File 'lib/net/dav.rb', line 465

def headers(headers)
  @headers = headers
end

#last_statusObject



19
20
21
# File 'lib/net/dav.rb', line 19

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>")


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

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)



726
727
728
729
730
# File 'lib/net/dav.rb', line 726

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)


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

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.



383
384
385
# File 'lib/net/dav.rb', line 383

def open_timeout
  @handler.read_timeout
end

#open_timeout=(sec) ⇒ Object



387
388
389
# File 'lib/net/dav.rb', line 387

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.



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

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>" +
  )


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

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



610
611
612
613
614
# File 'lib/net/dav.rb', line 610

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")


621
622
623
624
625
# File 'lib/net/dav.rb', line 621

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.



371
372
373
# File 'lib/net/dav.rb', line 371

def read_timeout
  @handler.read_timeout
end

#read_timeout=(sec) ⇒ Object



375
376
377
# File 'lib/net/dav.rb', line 375

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

#ssl_authority(ca_file) ⇒ Object

Set additional ssl authorities for ssl certificate authentication



459
460
461
462
# File 'lib/net/dav.rb', line 459

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



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

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


432
433
434
435
436
# File 'lib/net/dav.rb', line 432

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")


702
703
704
705
706
# File 'lib/net/dav.rb', line 702

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



732
733
734
# File 'lib/net/dav.rb', line 732

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

#verify_server=(value) ⇒ Object



736
737
738
# File 'lib/net/dav.rb', line 736

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