(* $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 *)