/*#*************************************************************************** # __ __ _____ _____ # Project | | | | | \ / ___| # | |__| | | |\ \ / / # | | | | ) ) ( ( # | /\ | | |/ / \ \___ # \_/ \_/ |_____/ \_____| # # Copyright (C) 2018, The WDC Project, , et al. # # This software is licensed as described in the file LICENSE, which # you should have received as part of this distribution. # # You may opt to use, copy, modify, merge, publish, distribute and/or sell # copies of the Software, and permit persons to whom the Software is # furnished to do so, under the terms of the LICENSE file. # # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY # KIND, either express or implied. # ############################################################################*/ #include #include "web/callback.hpp" #include "web/fsinfo.hpp" #include "web/header.hpp" #include "web/pugiext.hpp" #include "web/request.hpp" #include "web/urn.hpp" #include "util.h" #include "http/httplib.h" #include #include namespace WebDAV { using Web::Urn::Path; using Web::Request; using Web::Data; using Web::Header; using progress_funptr = int (*)(void *context, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { std::string header(reinterpret_cast(buffer), size * nitems); dict_t *headers = (dict_t *)userdata; size_t seperator = header.find_first_of(":"); if (seperator != std::string::npos) { std::string key = header.substr(0, seperator); key = Util::Trim(key, " "); key = Util::ToLower(key); std::string value = header.substr(seperator + 1); value = Util::Trim(value, " "); headers->erase(key); headers->insert(std::make_pair(key, value)); } return (size * nitems); } dict_t Client::options() { return dict_t{ {"hostname", this->webdav_hostname}, {"username", this->webdav_username}, {"password", this->webdav_password}, {"proxy_hostname", this->proxy_hostname}, {"proxy_username", this->proxy_username}, {"proxy_password", this->proxy_password}, {"cert_path", this->cert_path}, {"key_path", this->key_path}, }; } long Client::status_code() { return this->http_code; } bool Client::sync_download( const std::string &remote_file, const std::string &local_file, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = this->check(remote_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; std::ofstream file_stream(local_file, std::ios::binary); Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "GET"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HEADER, 0L); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&file_stream)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Write::stream)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); return is_performed; } bool Client::sync_download_to( const std::string &remote_file, char *&buffer_ptr, unsigned long long &buffer_size, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = this->check(remote_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Data data = {nullptr, 0, 0}; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "GET"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HEADER, 0L); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); if (!is_performed) return false; buffer_ptr = data.buffer; buffer_size = data.size; data.reset(); return true; } bool Client::sync_download_range_to( const std::string &remote_file, char *&buffer_ptr, unsigned long long &buffer_size, uint64_t range_from, uint64_t range_to, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = this->check(remote_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Data data = {nullptr, 0, 0}; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); struct curl_slist *list = NULL; char range_header[64]; sprintf(range_header, "Range: bytes=%lu-%lu", range_from, range_to); list = curl_slist_append(list, range_header); request.set(CURLOPT_CUSTOMREQUEST, "GET"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HEADER, 0L); request.set(CURLOPT_HTTPHEADER, list); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); if (!is_performed) return false; buffer_ptr = data.buffer; buffer_size = data.size; data.reset(); return true; } bool Client::sync_download_range_to( const std::string &remote_file, DataSink &sink, uint64_t range_from, uint64_t range_to, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = this->check(remote_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); struct curl_slist *list = NULL; char range_header[64]; sprintf(range_header, "Range: bytes=%lu-%lu", range_from, range_to); list = curl_slist_append(list, range_header); request.set(CURLOPT_CUSTOMREQUEST, "GET"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HEADER, 0L); request.set(CURLOPT_HTTPHEADER, list); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&sink)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::stream2sink)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); if (!is_performed) return false; return true; } bool Client::sync_download_to( const std::string &remote_file, std::ostream &stream, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = this->check(remote_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "GET"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HEADER, 0L); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&stream)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Write::stream)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); return is_performed; } bool Client::sync_upload( const std::string &remote_file, const std::string &local_file, callback_t callback, progress_data_t progress_data, progress_t progress) { bool is_existed = Web::FileInfo::exists(local_file); if (!is_existed) return false; auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; std::ifstream file_stream(local_file, std::ios::binary); auto size = Web::FileInfo::size(local_file); Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); Data response = {nullptr, 0, 0}; request.set(CURLOPT_UPLOAD, 1L); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_READDATA, reinterpret_cast(&file_stream)); request.set(CURLOPT_READFUNCTION, reinterpret_cast(Web::Callback::Read::stream)); request.set(CURLOPT_INFILESIZE_LARGE, static_cast(size)); request.set(CURLOPT_BUFFERSIZE, static_cast(Client::buffer_size)); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&response)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); return is_performed; } bool Client::sync_upload_from( const std::string &remote_file, char *buffer_ptr, unsigned long long buffer_size, callback_t callback, progress_data_t progress_data, progress_t progress) { auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Data data = {buffer_ptr, 0, buffer_size}; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); Data response = {nullptr, 0, 0}; request.set(CURLOPT_UPLOAD, 1L); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_READDATA, reinterpret_cast(&data)); request.set(CURLOPT_READFUNCTION, reinterpret_cast(Web::Callback::Read::buffer)); request.set(CURLOPT_INFILESIZE_LARGE, static_cast(buffer_size)); request.set(CURLOPT_BUFFERSIZE, static_cast(Client::buffer_size)); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&response)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); data.reset(); return is_performed; } bool Client::sync_upload_from( const std::string &remote_file, std::istream &stream, callback_t callback, progress_data_t progress_data, progress_t progress) { auto root_urn = Path(this->webdav_root, true); auto file_urn = root_urn + remote_file; Request request(this->options()); auto url = this->webdav_hostname + file_urn.quote(request.handle); stream.seekg(0, std::ios::end); size_t stream_size = stream.tellg(); stream.seekg(0, std::ios::beg); Data response = {nullptr, 0, 0}; request.set(CURLOPT_UPLOAD, 1L); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_READDATA, reinterpret_cast(&stream)); request.set(CURLOPT_READFUNCTION, reinterpret_cast(Web::Callback::Read::stream)); request.set(CURLOPT_INFILESIZE_LARGE, static_cast(stream_size)); request.set(CURLOPT_BUFFERSIZE, static_cast(Client::buffer_size)); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&response)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif if (progress != nullptr) { request.set(CURLOPT_XFERINFODATA, progress_data); request.set(CURLOPT_XFERINFOFUNCTION, progress); request.set(CURLOPT_NOPROGRESS, 0L); } bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (callback != nullptr) callback(is_performed); return is_performed; } Client::Client(const dict_t &options) { this->webdav_hostname = get(options, "webdav_hostname"); this->webdav_root = get(options, "webdav_root"); this->webdav_username = get(options, "webdav_username"); this->webdav_password = get(options, "webdav_password"); this->proxy_hostname = get(options, "proxy_hostname"); this->proxy_username = get(options, "proxy_username"); this->proxy_password = get(options, "proxy_password"); this->cert_path = get(options, "cert_path"); this->key_path = get(options, "key_path"); auto check = get(options, "check_enabled"); if (check.length() > 0) this->check_enabled = std::stoi(check); else this->check_enabled = 0; } unsigned long long Client::free_size() { Header header = { "Accept: */*", "Depth: 0", "Content-Type: text/xml"}; pugi::xml_document document; auto propfind = document.append_child("D:propfind"); propfind.append_attribute("xmlns:D") = "DAV:"; auto prop = propfind.append_child("D:prop"); prop.append_child("D:quokta-available-bytes"); prop.append_child("D:quota-used-bytes"); auto document_print = pugi::node_to_string(document); size_t size = document_print.length() * sizeof((document_print.c_str())[0]); Data data = {nullptr, 0, 0}; Request request(this->options()); request.set(CURLOPT_CUSTOMREQUEST, "PROPFIND"); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); request.set(CURLOPT_POSTFIELDS, document_print.c_str()); request.set(CURLOPT_POSTFIELDSIZE, static_cast(size)); request.set(CURLOPT_HEADER, 0); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif auto is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (!is_performed) return 0; document.load_buffer(data.buffer, static_cast(data.size)); pugi::xml_node multistatus = document.select_node("*[local-name()='multistatus']").node(); pugi::xml_node response = multistatus.select_node("*[local-name()='response']").node(); pugi::xml_node propstat = response.select_node("*[local-name()='propstat']").node(); prop = propstat.select_node("*[local-name()='prop']").node(); pugi::xml_node quota_available_bytes = prop.select_node("*[local-name()='quota-available-bytes']").node(); std::string free_size_text = quota_available_bytes.first_child().value(); return std::stoll(free_size_text); } bool Client::check(const std::string &remote_resource) { if (!this->check_enabled) return true; auto root_urn = Path(this->webdav_root, true); auto resource_urn = root_urn + remote_resource; Header header = { "Accept: */*", "Depth: 0"}; Data data = {nullptr, 0, 0}; Request request(this->options()); auto url = this->webdav_hostname + resource_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "PROPFIND"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } dict_t Client::info(const std::string &remote_resource) { auto root_urn = Path(this->webdav_root, true); auto target_urn = root_urn + remote_resource; Header header = { "Accept: */*", "Depth: 0"}; Data data = {nullptr, 0, 0}; Request request(this->options()); auto url = this->webdav_hostname + target_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "PROPFIND"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (!is_performed) return dict_t{}; pugi::xml_document document; document.load_buffer(data.buffer, static_cast(data.size)); #ifdef WDC_VERBOSE document.save(std::cout); #endif auto multistatus = document.select_node("*[local-name()='multistatus']").node(); auto responses = multistatus.select_nodes("*[local-name()='response']"); for (auto response : responses) { pugi::xml_node href = response.node().select_node("*[local-name()='href']").node(); std::string encode_file_name = href.first_child().value(); std::string resource_path = curl_unescape(encode_file_name.c_str(), static_cast(encode_file_name.length())); auto target_path = target_urn.path(); auto target_path_without_sep = target_urn.path(); if (!target_path_without_sep.empty() && target_path_without_sep.back() == '/') target_path_without_sep.resize(target_path_without_sep.length() - 1); auto resource_path_without_sep = resource_path.erase(resource_path.find_last_not_of('/') + 1); size_t pos = resource_path_without_sep.find(this->webdav_hostname); if (pos != std::string::npos) resource_path_without_sep.erase(pos, this->webdav_hostname.length()); if (resource_path_without_sep == target_path_without_sep) { auto propstat = response.node().select_node("*[local-name()='propstat']").node(); auto prop = propstat.select_node("*[local-name()='prop']").node(); auto creation_date = prop.select_node("*[local-name()='creationdate']").node(); auto display_name = prop.select_node("*[local-name()='displayname']").node(); auto content_length = prop.select_node("*[local-name()='getcontentlength']").node(); auto modified_date = prop.select_node("*[local-name()='getlastmodified']").node(); auto resource_type = prop.select_node("*[local-name()='resourcetype']").node(); std::string name = target_urn.name(); dict_t information = { {"created", creation_date.first_child().value()}, {"name", Util::Rtrim(name, "/")}, {"size", content_length.first_child().value()}, {"modified", modified_date.first_child().value()}, {"type", resource_type.first_child().name()}}; return information; } } return dict_t{}; } bool Client::head(const std::string &remote_resource, dict_t *headers) { auto root_urn = Path(this->webdav_root, true); auto target_urn = root_urn + remote_resource; Header header = {"Accept: */*"}; Request request(this->options()); auto url = this->webdav_hostname + target_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "HEAD"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); request.set(CURLOPT_HEADERDATA, headers); request.set(CURLOPT_HEADERFUNCTION, header_callback); request.set(CURLOPT_NOBODY, 1L); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } bool Client::is_directory(const std::string &remote_resource) { auto information = this->info(remote_resource); auto resource_type = information["type"]; bool is_dir = resource_type == "d:collection" || resource_type == "D:collection"; return is_dir; } dict_items_t Client::list(const std::string &remote_directory) { bool is_existed = this->check(remote_directory); if (!is_existed) return dict_items_t{}; auto target_urn = Path(this->webdav_root, true) + remote_directory; target_urn = Path(target_urn.path(), true); Header header = { "Accept: */*", "Depth: 1"}; Data data = {nullptr, 0, 0}; Request request(this->options()); auto url = this->webdav_hostname + target_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "PROPFIND"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); request.set(CURLOPT_HEADER, 0); request.set(CURLOPT_WRITEDATA, reinterpret_cast(&data)); request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast(Web::Callback::Append::buffer)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); if (!is_performed) return dict_items_t{}; dict_items_t resources; pugi::xml_document document; document.load_buffer(data.buffer, static_cast(data.size)); auto multistatus = document.select_node("*[local-name()='multistatus']").node(); auto responses = multistatus.select_nodes("*[local-name()='response']"); for (auto response : responses) { pugi::xml_node href = response.node().select_node("*[local-name()='href']").node(); std::string encode_file_name = href.first_child().value(); std::string resource_path = curl_unescape(encode_file_name.c_str(), static_cast(encode_file_name.length())); auto target_path = target_urn.path(); auto target_path_without_sep = target_urn.path(); if (!target_path_without_sep.empty() && target_path_without_sep.back() == '/') target_path_without_sep.resize(target_path_without_sep.length() - 1); auto resource_path_without_sep = resource_path.erase(resource_path.find_last_not_of('/') + 1); size_t pos = resource_path_without_sep.find(this->webdav_hostname); if (pos != std::string::npos) resource_path_without_sep.erase(pos, this->webdav_hostname.length()); if (resource_path_without_sep == target_path_without_sep) continue; auto propstat = response.node().select_node("*[local-name()='propstat']").node(); auto prop = propstat.select_node("*[local-name()='prop']").node(); auto creation_date = prop.select_node("*[local-name()='creationdate']").node(); auto display_name = prop.select_node("*[local-name()='displayname']").node(); auto content_length = prop.select_node("*[local-name()='getcontentlength']").node(); auto modified_date = prop.select_node("*[local-name()='getlastmodified']").node(); auto resource_type = prop.select_node("*[local-name()='resourcetype']").node(); Path resource_urn(resource_path); std::string name = resource_urn.name(); dict_t item = { {"created", creation_date.first_child().value()}, {"name", Util::Rtrim(name, "/")}, {"size", content_length.first_child().value()}, {"modified", modified_date.first_child().value()}, {"type", resource_type.first_child().name()}}; resources.push_back(item); } return resources; } bool Client::download( const std::string &remote_file, const std::string &local_file, progress_data_t progress_data, progress_t progress) { return this->sync_download(remote_file, local_file, nullptr, progress_data, std::move(progress)); } void Client::async_download( const std::string &remote_file, const std::string &local_file, callback_t callback, progress_data_t progress_data, progress_t progress) { std::thread downloading([=]() { this->sync_download(remote_file, local_file, callback, progress_data, std::move(progress)); }); downloading.detach(); } bool Client::download_to( const std::string &remote_file, char *&buffer_ptr, unsigned long long &buffer_size, progress_data_t progress_data, progress_t progress) { return this->sync_download_to(remote_file, buffer_ptr, buffer_size, nullptr, progress_data, std::move(progress)); } bool Client::download_range_to( const std::string &remote_file, char *&buffer_ptr, unsigned long long &buffer_size, uint64_t range_from, uint64_t range_to, progress_data_t progress_data, progress_t progress) { return this->sync_download_range_to(remote_file, buffer_ptr, buffer_size, range_from, range_to, nullptr, progress_data, std::move(progress)); } bool Client::download_range_to( const std::string &remote_file, DataSink &sink, uint64_t range_from, uint64_t range_to, progress_data_t progress_data, progress_t progress) { return this->sync_download_range_to(remote_file, sink, range_from, range_to, nullptr, progress_data, std::move(progress)); } bool Client::download_to( const std::string &remote_file, std::ostream &stream, progress_data_t progress_data, progress_t progress) { return this->sync_download_to(remote_file, stream, nullptr, progress_data, std::move(progress)); } bool Client::create_directory(const std::string &remote_directory, bool recursive) { bool is_existed = this->check(remote_directory); if (is_existed) return true; bool resource_is_dir = true; Path directory_urn(remote_directory, resource_is_dir); if (recursive) { auto remote_parent_directory = directory_urn.parent().path(); if (remote_parent_directory == remote_directory) return false; bool is_created = this->create_directory(remote_parent_directory, true); if (!is_created) return false; } Header header = { "Accept: */*", "Connection: Keep-Alive"}; auto target_urn = Path(this->webdav_root, true) + remote_directory; target_urn = Path(target_urn.path(), true); Request request(this->options()); auto url = this->webdav_hostname + target_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "MKCOL"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } bool Client::move(const std::string &remote_source_resource, const std::string &remote_destination_resource) { bool is_existed = this->check(remote_source_resource); if (!is_existed) return false; Path root_urn(this->webdav_root, true); auto source_resource_urn = root_urn + remote_source_resource; auto destination_resource_urn = root_urn + remote_destination_resource; Request request(this->options()); Header header = { "Accept: */*", "Destination: " + destination_resource_urn.quote(request.handle)}; auto url = this->webdav_hostname + source_resource_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "MOVE"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } bool Client::copy(const std::string &remote_source_resource, const std::string &remote_destination_resource) { bool is_existed = this->check(remote_source_resource); if (!is_existed) return false; Path root_urn(this->webdav_root, true); auto source_resource_urn = root_urn + remote_source_resource; auto destination_resource_urn = root_urn + remote_destination_resource; Request request(this->options()); Header header = { "Accept: */*", "Destination: " + destination_resource_urn.quote(request.handle)}; auto url = this->webdav_hostname + source_resource_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "COPY"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } bool Client::upload( const std::string &remote_file, const std::string &local_file, progress_data_t progress_data, progress_t progress) { return this->sync_upload(remote_file, local_file, nullptr, progress_data, std::move(progress)); } void Client::async_upload( const std::string &remote_file, const std::string &local_file, callback_t callback, progress_data_t progress_data, progress_t progress) { std::thread uploading([=]() { this->sync_upload(remote_file, local_file, callback, progress_data, std::move(progress)); }); uploading.detach(); } bool Client::upload_from( const std::string &remote_file, std::istream &stream, progress_data_t progress_data, progress_t progress) { return this->sync_upload_from(remote_file, stream, nullptr, progress_data, std::move(progress)); } bool Client::upload_from( const std::string &remote_file, char *buffer_ptr, unsigned long long buffer_size, progress_data_t progress_data, progress_t progress) { return this->sync_upload_from(remote_file, buffer_ptr, buffer_size, nullptr, progress_data, std::move(progress)); } bool Client::clean(const std::string &remote_resource) { bool is_existed = this->check(remote_resource); if (!is_existed) return true; auto root_urn = Path(this->webdav_root, true); auto resource_urn = root_urn + remote_resource; Header header = { "Accept: */*", "Connection: Keep-Alive"}; Request request(this->options()); auto url = this->webdav_hostname + resource_urn.quote(request.handle); request.set(CURLOPT_CUSTOMREQUEST, "DELETE"); request.set(CURLOPT_URL, url.c_str()); request.set(CURLOPT_HTTPHEADER, reinterpret_cast(header.handle)); #ifdef WDC_VERBOSE request.set(CURLOPT_VERBOSE, 1); #endif bool is_performed = request.perform(); this->http_code = request.status_code(); this->result = request.result(); return is_performed; } } // namespace WebDAV