Files
ps4-ezremote-client/source/webdav/client.cpp
T

1044 lines
33 KiB
C++

/*#***************************************************************************
# __ __ _____ _____
# Project | | | | | \ / ___|
# | |__| | | |\ \ / /
# | | | | ) ) ( (
# | /\ | | |/ / \ \___
# \_/ \_/ |_____/ \_____|
#
# Copyright (C) 2018, The WDC Project, <rusdevops@gmail.com>, 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 <webdav/client.hpp>
#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 <algorithm>
#include <thread>
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<char *>(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<size_t>(&file_stream));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<void*>(&sink));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&stream));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&file_stream));
request.set(CURLOPT_READFUNCTION, reinterpret_cast<size_t>(Web::Callback::Read::stream));
request.set(CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(size));
request.set(CURLOPT_BUFFERSIZE, static_cast<long>(Client::buffer_size));
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&response));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&data));
request.set(CURLOPT_READFUNCTION, reinterpret_cast<size_t>(Web::Callback::Read::buffer));
request.set(CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(buffer_size));
request.set(CURLOPT_BUFFERSIZE, static_cast<long>(Client::buffer_size));
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&response));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(&stream));
request.set(CURLOPT_READFUNCTION, reinterpret_cast<size_t>(Web::Callback::Read::stream));
request.set(CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(stream_size));
request.set(CURLOPT_BUFFERSIZE, static_cast<long>(Client::buffer_size));
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&response));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<struct curl_slist *>(header.handle));
request.set(CURLOPT_POSTFIELDS, document_print.c_str());
request.set(CURLOPT_POSTFIELDSIZE, static_cast<long>(size));
request.set(CURLOPT_HEADER, 0);
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(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<curl_slist *>(header.handle));
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<curl_slist *>(header.handle));
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(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<int>(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<curl_slist *>(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<curl_slist *>(header.handle));
request.set(CURLOPT_HEADER, 0);
request.set(CURLOPT_WRITEDATA, reinterpret_cast<size_t>(&data));
request.set(CURLOPT_WRITEFUNCTION, reinterpret_cast<size_t>(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<size_t>(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<int>(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<curl_slist *>(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<curl_slist *>(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<curl_slist *>(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<curl_slist *>(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