Plasma GitLab Archive
Projects Blog Knowledge

(* $Id: webdav_client.mli 9 2015-01-12 20:00:52Z gerd $ *)

(** WebDAV client (synchronous) *)

(** This module defines a simple synchronous WebDAV client.
    For async support, users should take the method classes in
    {!Webdav_client_methods} and push them onto {!Http_client}
    pipelines.
 *)

open Webdav_client_methods
open Webdav_http
open Webdav_compat

(** General remarks:

  {b Paths.}
     The paths passed to the access methods must be absolute
     (start with "/"), and must not include the [http://] prefix.
     All unsafe characters in the paths (like ";" or "?") are
     automatically percent-encoded.

  {b Synchronous operation.}
     The access methods return an object that already includes
     the servers response. All TCP connections are already closed.

  {b Depth.}
     The depth says whether the operation is only applied to the
     passed object ([`Zero]), to the object and the direct children
     ([`One]), or to the whole subtree ([`Infinity]). The latter is
     the default if the depth is not specified.

  {b Fixup.}
     All methods have a [fixup] argument. The passed function is called
     back with the call object before the WebDAV request is submitted to
     the server. This can be used to set options in the call object.

  {b Error checking.}
     The returned call object has methods reporting the exact error
     condition. For most applications it is sufficient to only
     check whether [fully_successful] is true. A number of methods
     can return partial successes, though, e.g. a recursive operation
     could only be carried out for a number of files. If this needs
     to be analyzed by the caller, one should first check [call_status]
     and if [`Multi_status] is returned, the methods [good_paths] and
     [bad_paths] list for which paths the operation was successful
     or not successful, respectively.
 *)
class type webdav_client_t = 
object
  method base_url : string
    (** The base URL this client class prepends to all URLs. For example,
        if the base URL is ["http://localhost/foo"] and the operation
        [propfind "/bar"] is invoked, actually the target URL is
        set to the concatation of both, namely ["http://localhost/foo/bar"].

        The base URL is encoded, if necessary (percent encoding).
     *)

  method pipeline : Http_client.pipeline
    (** The backing pipeline *)


  method propfind : ?depth:depth -> 
                    ?propfind_request:propfind_request -> 
                    ?fixup:(Http_client.http_call -> unit) ->
                    string -> propfind_call_t
    (** [propfind path]: Submits a [PROPFIND] request to the server.
        The returned [propfind_call_t] object already includes the 
        response.
     *)

  method list : ?depth:depth -> 
                ?fixup:(Http_client.http_call -> unit) ->
                list_request -> string -> list_t
    (** [list req path]: Submits a special [PROPFIND] request for
        getting a file listing.

        Example: Get file listing at [/foo]:
        {[
           let l = client # list `Existence "/foo" in
           if not l#fully_successful then failwith "Not successful";
           let paths = l#good_paths
        ]}

     *)

  method proppatch : ?fixup:(Http_client.http_call -> unit) ->
                     proppatch_request:proppatch_request ->
                     string -> proppatch_call_t
    (** [proppatch ~proppatch_request path]: Submits a [PROPPATCH]
	request to change properties. The change is described
	in [proppatch_request].

	Example: Set the [Content-Type]

	{[
	  let proppatch_request =
	    [ `Set [ Webdav_xml.encode_getcontenttype ("text/plain",[]) ] ] in
	  client # proppatch ~proppatch_request "/file"
	]}
     *)

  method mkcol : ?fixup:(Http_client.http_call -> unit) ->
                  string -> mkcol_call_t
    (** [mkcol path]: Submits an [MKCOL] request (create a new
        collection/directory).
     *)

  method delete : ?fixup:(Http_client.http_call -> unit) -> 
                  string -> delete_call_t
    (** [delete path]: Submits a [DELETE] request. Deletes are always
        recursive. Servers are allowed to delete as much as possible
        (instead of an "all or nothing" semantics), and to return
        partial success only.
     *)

  method get : ?store:Http_client.response_body_storage -> 
               ?fixup:(Http_client.http_call -> unit) -> 
               string -> get_call_t
    (** [get path]: Submits a [GET] request (download a file).
        The downloaded body can be accessed with the [response_body]
        method of the returned object.

        The [store] argument specifies where the body is stored.

        Example download (in-memory):
        {[
           let g = client # get "/path"
           let s = g # response_body # value
        ]}

        Example download to file:
        {[
           let store = `File (fun () -> "/home/gerd/downloaded_file")
           let g = client # get ~store "/path"
           let ch = g # response_body # open_value_rd()
           ch # input_line()    (* get first line *)
        ]}
     *)

  method put : ?content_type:string ->
               ?content_length:int64 ->
               ?expect_handshake:bool ->
               ?fixup:(Http_client.http_call -> unit) -> 
               string -> Netmime.mime_body -> put_call_t
    (** [put path body]: Submits a [PUT] request (upload a file).
        The [body] includes the data to upload.

      - [content_type]: The media type of the body. The server is free
        to ignore this.
      - [content_length]: The length of the body, if already known.
        There are servers that accept PUT only if the content length
        is set in the header. (See the function [length_of_body] below
        for getting the length.)
      - [expect_handshake]: Set this to [true] to enable a special
        handshake before the body is uploaded. This is reasonable
        when the PUT request may cause errors - the error can be received
        before the upload starts. There might be compatibility problems,
        though, as this feature was incorrectly specified in some versions
        of HTTP.

      Note that some servers do not permit that existing files are replaced.
      The RFC says nothing about this case, though.

      Example upload:
      {[
         let b = new Netmime.file_mime_body "/home/gerd/large.bin"
         let n = length_of_body b
         let p = client # put ~content_length:n ~expect_handshake:true
                               "/path" b
         if not p#fully_successful then
           failwith "Error"
      ]}
   *)

  method copy : ?depth:depth ->
                ?overwrite:bool ->
                ?dest_base_url:string ->
                ?fixup:(Http_client.http_call -> unit) -> 
                string -> string -> copy_call_t
    (** [copy src_path dest_path]: Submits a [COPY] request. By default,
        it is not possible to overwrite files during the copy. One has
        to set [overwrite:true] to allow this.

        Note that [dest_path] is interpreted as "copy onto", not "copy into"
        if it is a directory, i.e. the [src_path] subtree will be available
        also as [dest_path] if the copy is successful. This is different 
        from the semantics of the [cp] command.

        In a recursive copy, servers are free to copy only as many of the
        files as possible, i.e. partial success is possible.

        If [dest_base_url] is set, one can specify to copy to a different
        WebDAV server. This is rarely supported by servers, though.
     *)

  method move : ?overwrite:bool ->
                ?dest_base_url:string ->
                ?fixup:(Http_client.http_call -> unit) -> 
                string -> string -> move_call_t
    (** [move src_path dest_path]: Submits a [MOVE] request. By default,
        it is not possible to overwrite files during the move. One has
        to set [overwrite:true] to allow this.

        The [MOVE] method is always recursive.

        Note that [dest_path] is interpreted as "move onto", not "move into"
        if it is a directory, i.e. the [src_path] subtree will be available
        as [dest_path] if the move is successful. This is different 
        from the semantics of the [mv] command.

        Servers are free to move only as many of the
        files as possible, i.e. partial success is possible.

        If [dest_base_url] is set, one can specify to move to a different
        WebDAV server. This is rarely supported by servers, though.
     *)
end

class webdav_client :  ?pipeline : Http_client.pipeline ->
                       string -> 
                         webdav_client_t
  (** [new webdav_client base_url]: Creates a new WebDAV client with this
      base URL

      Example: [new webdav_client "http://server:8080/webdavroot"]
   *)


val webdav_client :  ?pipeline : Http_client.pipeline ->
                     string -> 
                        webdav_client_t
  (** Same as function *)			  

val url_path : string -> string
  (** Returns the decoded path of an encoded URL *)

val length_of_body : Netmime.mime_body -> int64
  (** Determine the length of a body *)

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml