--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/versioning.c Mon Jan 22 17:27:47 2024 +0100 @@ -0,0 +1,175 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "versioning.h" + +#include "methods.h" +#include "utils.h" +#include "session.h" + +static int basic_deltav_op(DavResource *res, char *method) { + DavSession *sn = res->session; + CURL *handle = sn->handle; + util_set_url(sn, dav_resource_get_href(res)); + + DavLock *lock = dav_get_lock(res->session, res->path); + char *locktoken = lock ? lock->token : NULL; + + CURLcode ret = do_simple_request(sn, method, locktoken); + long status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(!(ret == CURLE_OK && (status >= 200 && status < 300))) { + dav_session_set_error(sn, ret, status); + return 1; + } + return 0; +} + +int dav_versioncontrol(DavResource *res) { + return basic_deltav_op(res, "VERSION-CONTROL"); +} + +int dav_checkout(DavResource *res) { + return basic_deltav_op(res, "CHECKOUT"); +} + +int dav_checkin(DavResource *res) { + return basic_deltav_op(res, "CHECKIN"); +} + +int dav_uncheckout(DavResource *res) { + return basic_deltav_op(res, "UNCHECKOUT"); +} + +DavResource* dav_versiontree(DavResource *res, char *properties) { + DavSession *sn = res->session; + util_set_url(sn, dav_resource_get_href(res)); + + CxList *proplist = NULL; + if(properties) { + proplist = parse_properties_string(sn->context, cx_str(properties)); + + // check if the list already contains a D:version-name property + int add_vname = 1; + CxIterator i = cxListIterator(proplist); + cx_foreach(DavProperty *, p, i) { + if(!strcmp(p->ns->name, "DAV:") && !strcmp(p->name, "version-name")) { + add_vname = 0; + break; + } + } + if(add_vname) { + // we need at least the D:version-name prop + DavProperty p; + p.ns = dav_get_namespace(sn->context, "D"); + p.name = strdup("version-name"); + p.value = NULL; + cxListInsert(proplist, 0, &p); + } + } + + + + // create a version-tree request, which is almost the same as propfind + CxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1); + CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + + // do the request + CURLcode ret = do_report_request(sn, rqbuf, rpbuf); + long status = 0; + curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); + int error = 0; + DavResource *versions = NULL; + if(ret == CURLE_OK && status == 207) { + sn->error = DAV_OK; + + // parse multistatus response + PropfindParser *parser = create_propfind_parser(rpbuf, NULL); + if(parser) { + DavResource *list_end = NULL; + + ResponseTag response; + int r; + + // we don't want name decryption for version resources + int snflags = sn->flags; + sn->flags = 0; + while((r = get_propfind_response(parser, &response)) != 0) { + if(r == -1) { + res->session->error = DAV_ERROR; + error = 1; + break; + } + DavResource *v = response2resource(sn, &response, NULL); + // add version to list + if(!versions) { + versions = v; + } else { + list_end->next = v; + } + list_end = v; + + cleanup_response(&response); + } + sn->flags = snflags; + + destroy_propfind_parser(parser); + } else { + sn->error = DAV_ERROR; + error = 1; + } + } else { + dav_session_set_error(sn, ret, status); + error = 1; + } + + // cleanup + if(proplist) { + CxIterator i = cxListIterator(proplist); + cx_foreach(DavProperty*, p, i) { + free(p->name); + } + cxListDestroy(proplist); + } + + if(error && versions) { + DavResource *cur = versions; + while(cur) { + DavResource *next = cur->next; + dav_resource_free(cur); + cur = next; + } + versions = NULL; + } + + return versions; +}