mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-09-09 02:55:23 -05:00
remote::Storage just barely working.
This commit is contained in:
45
source/remote/Form.cpp
Normal file
45
source/remote/Form.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "remote/Form.hpp"
|
||||
|
||||
remote::Form::Form(const remote::Form &form)
|
||||
{
|
||||
m_form = form.m_form;
|
||||
}
|
||||
|
||||
remote::Form::Form(remote::Form &&form)
|
||||
{
|
||||
m_form = form.m_form;
|
||||
form.m_form.clear();
|
||||
}
|
||||
|
||||
remote::Form &remote::Form::operator=(const remote::Form &form)
|
||||
{
|
||||
m_form = form.m_form;
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::Form &remote::Form::operator=(remote::Form &&form)
|
||||
{
|
||||
m_form = form.m_form;
|
||||
form.m_form.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::Form &remote::Form::append_parameter(std::string_view param, std::string_view value)
|
||||
{
|
||||
if (!m_form.empty() && m_form.back() != '&')
|
||||
{
|
||||
m_form.append("&");
|
||||
}
|
||||
m_form.append(param).append("=").append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char *remote::Form::get() const
|
||||
{
|
||||
return m_form.c_str();
|
||||
}
|
||||
|
||||
size_t remote::Form::length() const
|
||||
{
|
||||
return m_form.length();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,29 @@
|
||||
#include "remote/Item.hpp"
|
||||
|
||||
remote::Item::Item(std::string_view name, std::string_view id, std::string_view parent, bool directory)
|
||||
: m_name(name), m_id(id), m_parent(parent), m_isDirectory(directory) {};
|
||||
remote::Item::Item(std::string_view name, std::string_view id, std::string_view parent, size_t size, bool directory)
|
||||
: m_name(name), m_id(id), m_parent(parent), m_size(size), m_isDirectory(directory) {};
|
||||
|
||||
std::string_view remote::Item::get_name(void) const
|
||||
std::string_view remote::Item::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
std::string_view remote::Item::get_id(void) const
|
||||
std::string_view remote::Item::get_id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
std::string_view remote::Item::get_parent_id(void) const
|
||||
std::string_view remote::Item::get_parent_id() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
size_t remote::Item::get_size(void) const
|
||||
size_t remote::Item::get_size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool remote::Item::is_directory(void) const
|
||||
bool remote::Item::is_directory() const
|
||||
{
|
||||
return m_isDirectory;
|
||||
}
|
||||
|
||||
@@ -1,51 +1,87 @@
|
||||
#include "remote/Storage.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
bool remote::Storage::is_initialized(void) const
|
||||
// Declarations here. Defined at bottom.
|
||||
remote::Storage::Storage() : m_curl(curl::new_handle()) {};
|
||||
|
||||
bool remote::Storage::is_initialized() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
|
||||
bool remote::Storage::directory_exists(std::string_view name)
|
||||
{
|
||||
return Storage::find_directory(name) != m_list.end();
|
||||
return Storage::find_directory_by_name(name) != m_list.end();
|
||||
}
|
||||
|
||||
bool remote::Storage::get_directory_id(std::string_view name, std::string &idOut)
|
||||
void remote::Storage::return_to_root()
|
||||
{
|
||||
remote::Storage::List::iterator findDir = Storage::find_directory(name);
|
||||
if (findDir == m_list.end())
|
||||
m_parent = m_root;
|
||||
}
|
||||
|
||||
void remote::Storage::set_root_directory(remote::Item *root)
|
||||
{
|
||||
m_root = root->get_id();
|
||||
}
|
||||
|
||||
void remote::Storage::change_directory(remote::Item *item)
|
||||
{
|
||||
m_parent = item->get_id();
|
||||
}
|
||||
|
||||
remote::Item *remote::Storage::get_directory_by_name(std::string_view name)
|
||||
{
|
||||
auto findDirectory = Storage::find_directory_by_name(name);
|
||||
if (findDirectory == m_list.end())
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
idOut = findDir->get_id();
|
||||
return true;
|
||||
return &(*findDirectory);
|
||||
}
|
||||
|
||||
remote::Storage::DirectoryListing remote::Storage::get_directory_listing()
|
||||
{
|
||||
remote::Storage::DirectoryListing listing;
|
||||
|
||||
Storage::List::iterator current = m_list.begin();
|
||||
while ((current = std::find_if(current, m_list.end(), [this](const Item &item) {
|
||||
return item.get_parent_id() == this->m_parent;
|
||||
})) != m_list.end())
|
||||
{
|
||||
listing.push_back(&(*current));
|
||||
}
|
||||
|
||||
return listing;
|
||||
}
|
||||
|
||||
bool remote::Storage::file_exists(std::string_view name)
|
||||
{
|
||||
return Storage::find_file(name) != m_list.end();
|
||||
return Storage::find_file_by_name(name) != m_list.end();
|
||||
}
|
||||
|
||||
bool remote::Storage::get_file_id(std::string_view name, std::string &idOut)
|
||||
remote::Item *remote::Storage::get_file_by_name(std::string_view name)
|
||||
{
|
||||
remote::Storage::List::iterator findFile = Storage::find_file(name);
|
||||
auto findFile = Storage::find_file_by_name(name);
|
||||
if (findFile == m_list.end())
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
idOut = findFile->get_id();
|
||||
return true;
|
||||
return &(*findFile);
|
||||
}
|
||||
|
||||
remote::Storage::List::iterator remote::Storage::find_directory(std::string_view name)
|
||||
bool remote::Storage::supports_utf8() const
|
||||
{
|
||||
return m_utf8Paths;
|
||||
}
|
||||
|
||||
remote::Storage::List::iterator remote::Storage::find_directory_by_name(std::string_view name)
|
||||
{
|
||||
return std::find_if(m_list.begin(), m_list.end(), [name, this](const Item &item) {
|
||||
return item.is_directory() && item.get_parent_id() == this->m_parent && item.get_name() == name;
|
||||
});
|
||||
}
|
||||
|
||||
remote::Storage::List::iterator remote::Storage::find_file(std::string_view name)
|
||||
remote::Storage::List::iterator remote::Storage::find_file_by_name(std::string_view name)
|
||||
{
|
||||
return std::find_if(m_list.begin(), m_list.end(), [name, this](const Item &item) {
|
||||
return !item.is_directory() && item.get_parent_id() == this->m_parent && item.get_name() == name;
|
||||
|
||||
90
source/remote/URL.cpp
Normal file
90
source/remote/URL.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "remote/URL.hpp"
|
||||
|
||||
remote::URL::URL(std::string_view base) : m_url(base) {};
|
||||
|
||||
remote::URL::URL(const URL &url)
|
||||
{
|
||||
m_url = url.m_url;
|
||||
}
|
||||
|
||||
remote::URL::URL(URL &&url)
|
||||
{
|
||||
m_url = url.m_url;
|
||||
// This seems odd, but w/e
|
||||
url.m_url.clear();
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::operator=(const remote::URL &url)
|
||||
{
|
||||
m_url = url.m_url;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::operator=(remote::URL &&url)
|
||||
{
|
||||
m_url = url.m_url;
|
||||
url.m_url.clear();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::set_base(std::string_view base)
|
||||
{
|
||||
// This will just assign and clear out the old one, I hope.
|
||||
m_url = base;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::append_path(std::string_view path)
|
||||
{
|
||||
// Check both just to be sure because this makes WebDav easier to tackle.
|
||||
if (m_url.back() != '/' && path.front() != '/')
|
||||
{
|
||||
m_url.append("/");
|
||||
}
|
||||
|
||||
// This is here to make WebDav easier to read and deal with in case of blank basepaths.
|
||||
if (path.empty())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_url.append(path);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::append_parameter(std::string_view param, std::string_view value)
|
||||
{
|
||||
URL::append_separator();
|
||||
m_url.append(param).append("=").append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
remote::URL &remote::URL::append_slash()
|
||||
{
|
||||
if (m_url.back() != '/')
|
||||
{
|
||||
m_url.append("/");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char *remote::URL::get() const
|
||||
{
|
||||
return m_url.c_str();
|
||||
}
|
||||
|
||||
void remote::URL::append_separator()
|
||||
{
|
||||
if (m_url.find('?') == m_url.npos)
|
||||
{
|
||||
m_url.append("?");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_url.append("&");
|
||||
}
|
||||
}
|
||||
390
source/remote/WebDav.cpp
Normal file
390
source/remote/WebDav.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
#include "remote/WebDav.hpp"
|
||||
#include "JSON.hpp"
|
||||
#include "curl/curl.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "remote/remote.hpp"
|
||||
#include <tinyxml2.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *TAG_XML_HREF = "href";
|
||||
}
|
||||
|
||||
// Declarations here. Definitions at bottom.
|
||||
/// @brief Gets a XML element by name. This is namespace agnostic.
|
||||
/// @param parent Parent XML element.
|
||||
/// @param name Name of the element to get. No namespace needed.
|
||||
static tinyxml2::XMLElement *get_element_by_name(tinyxml2::XMLElement *parent, std::string_view name);
|
||||
|
||||
/// @brief Returns the starting point of the tag name without the namespace.
|
||||
/// @param tag Tag to get the name of.
|
||||
static std::string_view get_tag_begin(std::string_view tag);
|
||||
|
||||
remote::WebDav::WebDav() : Storage()
|
||||
{
|
||||
static const char *STRING_CONFIG_READ_ERROR = "Error initializing WebDav: %s";
|
||||
|
||||
// WebDav has problems with these.
|
||||
m_utf8Paths = false;
|
||||
|
||||
json::Object config = json::new_object(json_object_from_file, remote::PATH_WEBDAV_CONFIG.data());
|
||||
if (!config)
|
||||
{
|
||||
logger::log(STRING_CONFIG_READ_ERROR, "Error reading configuration file!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's just get this all out of the way at once.
|
||||
json_object *origin = json::get_object(config, "origin");
|
||||
json_object *basepath = json::get_object(config, "basepath");
|
||||
json_object *username = json::get_object(config, "username");
|
||||
json_object *password = json::get_object(config, "password");
|
||||
|
||||
// This is the bare minimum required to continue.
|
||||
if (!origin)
|
||||
{
|
||||
logger::log(STRING_CONFIG_READ_ERROR, "Config is missing origin!");
|
||||
return;
|
||||
}
|
||||
m_origin = json_object_get_string(origin);
|
||||
|
||||
if (basepath)
|
||||
{
|
||||
// The root is both in the beginning. I want this to work as closely as the original just not as poorly written
|
||||
// or thought out as the original JKSV dav code.
|
||||
m_root = json_object_get_string(basepath);
|
||||
m_parent = m_root;
|
||||
}
|
||||
|
||||
if (username)
|
||||
{
|
||||
m_username = json_object_get_string(username);
|
||||
}
|
||||
|
||||
if (password)
|
||||
{
|
||||
m_password = json_object_get_string(password);
|
||||
}
|
||||
|
||||
// This is the starting point. This will read the entire basepath listing in one go.
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_root).append_slash();
|
||||
|
||||
// This'll recursively get the full listing of the basepath for the WebDav server.
|
||||
std::string xml{};
|
||||
if (!WebDav::prop_find(url, xml) || !WebDav::process_listing(xml))
|
||||
{
|
||||
logger::log(STRING_CONFIG_READ_ERROR, "Error retrieving listing from WebDav server!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
bool remote::WebDav::create_directory(std::string_view name)
|
||||
{
|
||||
static const char *STRING_CREATE_DIR_ERROR = "Error creating WebDav directory: %s";
|
||||
|
||||
std::string escapedName;
|
||||
if (!curl::escape_string(m_curl, name, escapedName))
|
||||
{
|
||||
logger::log(STRING_CREATE_DIR_ERROR, "Error escaping directory name!");
|
||||
return false;
|
||||
}
|
||||
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_parent).append_path(escapedName).append_slash();
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
curl::set_option(m_curl, CURLOPT_CUSTOMREQUEST, "MKCOL");
|
||||
|
||||
if (!curl::perform(m_curl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (curl::get_response_code(m_curl) != 201)
|
||||
{
|
||||
logger::log(STRING_CREATE_DIR_ERROR, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is the ID string so we can make WebDav work within the same framework as Google Drive.
|
||||
std::string id = m_parent + "/" + escapedName + "/";
|
||||
m_list.emplace_back(name, id, m_parent, 0, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remote::WebDav::upload_file(const fslib::Path &source)
|
||||
{
|
||||
static const char *STRING_ERROR_UPLOADING = "Error uploading file: %s";
|
||||
|
||||
fslib::File file(source, FsOpenMode_Read);
|
||||
if (!file)
|
||||
{
|
||||
logger::log(STRING_ERROR_UPLOADING, fslib::get_error_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string escapedName;
|
||||
if (!curl::escape_string(m_curl, source.get_filename(), escapedName))
|
||||
{
|
||||
logger::log(STRING_ERROR_UPLOADING, "Failed to escape filename!");
|
||||
return false;
|
||||
}
|
||||
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_parent).append_path(escapedName);
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
curl::set_option(m_curl, CURLOPT_UPLOAD, 1L);
|
||||
curl::set_option(m_curl, CURLOPT_UPLOAD_BUFFERSIZE, Storage::SIZE_UPLOAD_BUFFER);
|
||||
curl::set_option(m_curl, CURLOPT_READFUNCTION, curl::read_data_from_file);
|
||||
curl::set_option(m_curl, CURLOPT_READDATA, &file);
|
||||
|
||||
if (!curl::perform(m_curl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_list.emplace_back(source.get_filename(), escapedName, m_parent, file.get_size(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remote::WebDav::patch_file(remote::Item *item, const fslib::Path &source)
|
||||
{
|
||||
static const char *STRING_ERROR_PATCHING = "Error patching file: %s";
|
||||
|
||||
fslib::File file(source, FsOpenMode_Read);
|
||||
if (!file)
|
||||
{
|
||||
logger::log(STRING_ERROR_PATCHING, fslib::get_error_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_parent).append_path(item->get_id());
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
curl::set_option(m_curl, CURLOPT_UPLOAD, 1L);
|
||||
curl::set_option(m_curl, CURLOPT_UPLOAD_BUFFERSIZE, Storage::SIZE_UPLOAD_BUFFER);
|
||||
curl::set_option(m_curl, CURLOPT_READFUNCTION, curl::read_data_from_file);
|
||||
curl::set_option(m_curl, CURLOPT_READDATA, &file);
|
||||
|
||||
if (!curl::perform(m_curl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just update the size this time.
|
||||
item->set_size(file.get_size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remote::WebDav::download_file(const remote::Item *item, const fslib::Path &destination)
|
||||
{
|
||||
static const char *STRING_ERROR_DOWNLOADING = "Error downloading file: %s";
|
||||
|
||||
fslib::File file(destination, FsOpenMode_Create | FsOpenMode_Write);
|
||||
if (!file)
|
||||
{
|
||||
logger::log(STRING_ERROR_DOWNLOADING, fslib::get_error_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_parent).append_path(item->get_id());
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_HTTPGET, 1L);
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
curl::set_option(m_curl, CURLOPT_WRITEFUNCTION, curl::write_data_to_file);
|
||||
curl::set_option(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
|
||||
if (!curl::perform(m_curl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remote::WebDav::delete_item(const remote::Item *item)
|
||||
{
|
||||
static const char *STRING_ERROR_DELETING = "Error deleting item: %s";
|
||||
|
||||
remote::URL url{m_origin};
|
||||
url.append_path(m_parent).append_path(item->get_id());
|
||||
if (item->is_directory())
|
||||
{
|
||||
url.append_slash();
|
||||
}
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
|
||||
if (!curl::perform(m_curl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (curl::get_response_code(m_curl) != 204)
|
||||
{
|
||||
logger::log(STRING_ERROR_DELETING, "Deletion failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void remote::WebDav::append_credentials()
|
||||
{
|
||||
if (!m_username.empty())
|
||||
{
|
||||
curl::set_option(m_curl, CURLOPT_USERNAME, m_username.c_str());
|
||||
}
|
||||
|
||||
if (!m_password.empty())
|
||||
{
|
||||
curl::set_option(m_curl, CURLOPT_PASSWORD, m_password.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool remote::WebDav::prop_find(const remote::URL &url, std::string &xml)
|
||||
{
|
||||
// Some servers block Depth: Infinity.
|
||||
curl::HeaderList header = curl::new_header_list();
|
||||
curl::append_header(header, "Depth: 1");
|
||||
|
||||
curl::reset_handle(m_curl);
|
||||
WebDav::append_credentials();
|
||||
curl::set_option(m_curl, CURLOPT_CUSTOMREQUEST, "PROPFIND");
|
||||
curl::set_option(m_curl, CURLOPT_HTTPHEADER, header.get());
|
||||
curl::set_option(m_curl, CURLOPT_URL, url.get());
|
||||
curl::set_option(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
|
||||
curl::set_option(m_curl, CURLOPT_WRITEDATA, &xml);
|
||||
|
||||
return curl::perform(m_curl);
|
||||
}
|
||||
|
||||
bool remote::WebDav::process_listing(std::string_view xml)
|
||||
{
|
||||
static const char *STRING_ERROR_PROCESSING_XML = "Error processing XML: %s";
|
||||
|
||||
tinyxml2::XMLDocument listing{};
|
||||
if (listing.Parse(xml.data(), xml.length()))
|
||||
{
|
||||
logger::log(STRING_ERROR_PROCESSING_XML, "Couldn't parse XML!");
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *root = listing.RootElement();
|
||||
|
||||
// The first element is what we're using as the parent.
|
||||
tinyxml2::XMLElement *parent = root->FirstChildElement();
|
||||
|
||||
tinyxml2::XMLElement *parentLocation = get_element_by_name(parent, TAG_XML_HREF);
|
||||
if (!parentLocation)
|
||||
{
|
||||
logger::log(STRING_ERROR_PROCESSING_XML, "Error finding list parent location!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// There's no point in continuing if this fails. Just return true.
|
||||
tinyxml2::XMLElement *current = parent->NextSiblingElement();
|
||||
if (!current)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// Parsing XML is actually annoying. Even with tinyxml2.
|
||||
tinyxml2::XMLElement *href = get_element_by_name(current, TAG_XML_HREF);
|
||||
tinyxml2::XMLElement *propstat = get_element_by_name(current, "propstat");
|
||||
tinyxml2::XMLElement *prop = get_element_by_name(propstat, "prop");
|
||||
tinyxml2::XMLElement *resourceType = get_element_by_name(prop, "resourcetype");
|
||||
if (!href || !propstat || !prop || !resourceType)
|
||||
{
|
||||
logger::log(STRING_ERROR_PROCESSING_XML, "Element is missing data!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is the best way to detect a directory.
|
||||
tinyxml2::XMLElement *collection = get_element_by_name(resourceType, "collection");
|
||||
if (collection)
|
||||
{
|
||||
// JKSV doesn't expose folder names to the end user, so they are emplaced as-is.
|
||||
m_list.emplace_back(href->GetText(), href->GetText(), parentLocation->GetText(), 0, true);
|
||||
|
||||
remote::URL nextUrl{m_origin};
|
||||
nextUrl.append_path(href->GetText());
|
||||
|
||||
std::string xml{};
|
||||
if (!WebDav::prop_find(nextUrl, xml) || !WebDav::process_listing(xml))
|
||||
{
|
||||
logger::log(STRING_ERROR_PROCESSING_XML, href->GetText());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tinyxml2::XMLElement *displayName = get_element_by_name(prop, "displayname");
|
||||
tinyxml2::XMLElement *getContentLength = get_element_by_name(prop, "getcontentlength");
|
||||
if (!displayName || !getContentLength)
|
||||
{
|
||||
logger::log(STRING_ERROR_PROCESSING_XML, "Missing needed tags for file!");
|
||||
continue;
|
||||
}
|
||||
|
||||
m_list.emplace_back(displayName->GetText(),
|
||||
href->GetText(),
|
||||
parentLocation->GetText(),
|
||||
std::strtoll(getContentLength->GetText(), NULL, 10),
|
||||
false);
|
||||
}
|
||||
} while ((current = current->NextSiblingElement()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static tinyxml2::XMLElement *get_element_by_name(tinyxml2::XMLElement *parent, std::string_view name)
|
||||
{
|
||||
tinyxml2::XMLElement *current = parent->FirstChildElement();
|
||||
if (!current)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (get_tag_begin(current->Name()) == name)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
} while ((current = current->NextSiblingElement()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::string_view get_tag_begin(std::string_view tag)
|
||||
{
|
||||
size_t colon = tag.find_first_of(':');
|
||||
if (colon == tag.npos)
|
||||
{
|
||||
return tag;
|
||||
}
|
||||
return tag.substr(colon + 1);
|
||||
}
|
||||
140
source/remote/remote.cpp
Normal file
140
source/remote/remote.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "remote/remote.hpp"
|
||||
#include "StateManager.hpp"
|
||||
#include "appStates/TaskState.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "remote/GoogleDrive.hpp"
|
||||
#include "remote/WebDav.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "ui/PopMessageManager.hpp"
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace
|
||||
{
|
||||
/// @brief This is just the string for finding and creating the JKSV dir.
|
||||
const char *STRING_JKSV_DIR = "JKSV";
|
||||
|
||||
/// @brief This is the single (for now) instance of a storage class.
|
||||
std::unique_ptr<remote::Storage> s_storage = nullptr;
|
||||
} // namespace
|
||||
|
||||
// Declarations here. Definitions at bottom.
|
||||
/// @brief This is the thread function that handles logging into Google.
|
||||
static void drive_sign_in(sys::Task *task, remote::GoogleDrive *drive);
|
||||
|
||||
/// @brief This creates (if needed) the JKSV folder for Google Drive and sets it as the root.
|
||||
/// @param drive Pointer to the drive instance..
|
||||
static void drive_set_jksv_root(remote::GoogleDrive *drive);
|
||||
|
||||
void remote::initialize_google_drive()
|
||||
{
|
||||
// Create drive instance.
|
||||
s_storage = std::make_unique<remote::GoogleDrive>();
|
||||
|
||||
// Need to cast this for it to work right.
|
||||
remote::GoogleDrive *drive = static_cast<remote::GoogleDrive *>(s_storage.get());
|
||||
|
||||
if (drive->sign_in_required())
|
||||
{
|
||||
auto signIn = std::make_shared<TaskState>(drive_sign_in, drive);
|
||||
StateManager::push_state(signIn);
|
||||
// We can return here because the task should handle the rest of the setup for us.
|
||||
return;
|
||||
}
|
||||
|
||||
// To do: Handle this better. Maybe retry somehow?
|
||||
if (!drive->is_initialized())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Can't forget this.
|
||||
drive_set_jksv_root(drive);
|
||||
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
|
||||
strings::get_by_name(strings::names::GOOGLE_DRIVE_STRINGS, 1));
|
||||
}
|
||||
|
||||
void remote::initialize_webdav()
|
||||
{
|
||||
s_storage = std::make_unique<remote::WebDav>();
|
||||
|
||||
if (s_storage->is_initialized())
|
||||
{
|
||||
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
|
||||
strings::get_by_name(strings::names::WEBDAV_STRINGS, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
|
||||
strings::get_by_name(strings::names::WEBDAV_STRINGS, 1));
|
||||
}
|
||||
}
|
||||
|
||||
remote::Storage *remote::get_remote_storage()
|
||||
{
|
||||
if (!s_storage || !s_storage->is_initialized())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return s_storage.get();
|
||||
}
|
||||
|
||||
static void drive_sign_in(sys::Task *task, remote::GoogleDrive *drive)
|
||||
{
|
||||
static const char *STRING_ERROR_SIGNING_IN = "Error signing into Google Drive: %s";
|
||||
|
||||
std::string message{}, deviceCode{};
|
||||
std::time_t expiration = 0;
|
||||
int pollingInterval = 0;
|
||||
|
||||
if (!drive->get_sign_in_data(message, deviceCode, expiration, pollingInterval))
|
||||
{
|
||||
logger::log(STRING_ERROR_SIGNING_IN, "Getting sign in data failed!");
|
||||
task->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
task->set_status(message.c_str());
|
||||
|
||||
while (std::time(NULL) < expiration && !drive->poll_sign_in(deviceCode))
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(pollingInterval));
|
||||
}
|
||||
|
||||
if (drive->is_initialized())
|
||||
{
|
||||
// Run this quick so the root is set correctly.
|
||||
drive_set_jksv_root(drive);
|
||||
// Show everyone I did it!
|
||||
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
|
||||
strings::get_by_name(strings::names::GOOGLE_DRIVE_STRINGS, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
|
||||
strings::get_by_name(strings::names::GOOGLE_DRIVE_STRINGS, 2));
|
||||
}
|
||||
|
||||
task->finished();
|
||||
}
|
||||
|
||||
static void drive_set_jksv_root(remote::GoogleDrive *drive)
|
||||
{
|
||||
static const char *STRING_ERROR_SETTING_DIR = "Error creating/setting JKSV directory on Drive: %s";
|
||||
|
||||
if (!drive->directory_exists(STRING_JKSV_DIR) && !drive->create_directory(STRING_JKSV_DIR))
|
||||
{
|
||||
logger::log(STRING_ERROR_SETTING_DIR, "Error finding and/or creating directory!");
|
||||
return;
|
||||
}
|
||||
|
||||
remote::Item *jksvDir = drive->get_directory_by_name(STRING_JKSV_DIR);
|
||||
if (!jksvDir)
|
||||
{
|
||||
logger::log(STRING_ERROR_SETTING_DIR, "Error locating directory in list! This shouldn't be able to happen!");
|
||||
return;
|
||||
}
|
||||
drive->set_root_directory(jksvDir);
|
||||
drive->change_directory(jksvDir);
|
||||
}
|
||||
Reference in New Issue
Block a user