diff --git a/CMakeLists.txt b/CMakeLists.txt index 457b6be..f6d824c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(ezremote_client source/http/httplib.cpp source/clients/baseclient.cpp source/clients/apache.cpp + source/clients/archiveorg.cpp source/clients/ftpclient.cpp source/clients/gdrive.cpp source/clients/iis.cpp diff --git a/source/actions.cpp b/source/actions.cpp index 7b15672..ede80a6 100644 --- a/source/actions.cpp +++ b/source/actions.cpp @@ -10,6 +10,7 @@ #include "clients/smbclient.h" #include "clients/webdav.h" #include "clients/apache.h" +#include "clients/archiveorg.h" #include "clients/nginx.h" #include "clients/npxserve.h" #include "clients/nfsclient.h" @@ -1246,6 +1247,8 @@ namespace Actions remoteclient = new NpxServeClient(); else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_RCLONE) == 0) remoteclient = new RCloneClient(); + else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_ARCHIVEORG) == 0) + remoteclient = new ArchiveOrgClient(); } else if (strncmp(remote_settings->server, "webdavs://", 10) == 0 || strncmp(remote_settings->server, "webdav://", 9) == 0) { diff --git a/source/clients/archiveorg.cpp b/source/clients/archiveorg.cpp new file mode 100644 index 0000000..19ce014 --- /dev/null +++ b/source/clients/archiveorg.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include "common.h" +#include "clients/remote_client.h" +#include "clients/archiveorg.h" +#include "lang.h" +#include "util.h" +#include "system.h" +#include "windows.h" + +using httplib::Client; +using httplib::Headers; +using httplib::Result; + +static std::map month_map = {{"Jan", 1}, {"Feb", 2}, {"Mar", 3}, {"Apr", 4}, {"May", 5}, {"Jun", 6}, {"Jul", 7}, {"Aug", 8}, {"Sep", 9}, {"Oct", 10}, {"Nov", 11}, {"Dec", 12}}; + +std::vector ArchiveOrgClient::ListDir(const std::string &path) +{ + std::vector out; + DirEntry entry; + Util::SetupPreviousFolder(path, &entry); + out.push_back(entry); + + std::string encoded_path = httplib::detail::encode_url(GetFullPath(path) + "/"); + if (auto res = client->Get(encoded_path)) + { + lxb_status_t status; + lxb_dom_attr_t *attr; + lxb_dom_element_t *table_element, *tr_element, *td_element; + lxb_html_document_t *document; + lxb_dom_collection_t *table_collection; + lxb_dom_collection_t *tr_collection; + lxb_dom_collection_t *td_collection; + std::string tmp_string; + const lxb_char_t *value; + size_t value_len; + + document = lxb_html_document_create(); + status = lxb_html_document_parse(document, (lxb_char_t *)res->body.c_str(), res->body.length()); + if (status != LXB_STATUS_OK) + { + lxb_html_document_destroy(document); + goto finish; + } + + table_collection = lxb_dom_collection_make(&document->dom_document, 1); + if (table_collection == NULL) + { + lxb_html_document_destroy(document); + goto finish; + } + + tr_collection = lxb_dom_collection_make(&document->dom_document, 128); + if (tr_collection == NULL) + { + lxb_html_document_destroy(document); + goto finish; + } + + status = lxb_dom_elements_by_tag_name(lxb_dom_interface_element(document->body), + table_collection, (const lxb_char_t *)"table", 5); + if (status != LXB_STATUS_OK) + { + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + + if (lxb_dom_collection_length(table_collection) < 1) + { + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + + for (size_t i = 0; i < lxb_dom_collection_length(table_collection); i++) + { + table_element = lxb_dom_collection_element(table_collection, i); + value = lxb_dom_element_class(table_element, &value_len); + tmp_string = std::string((const char *)value, value_len); + if (tmp_string.compare("directory-listing-table") == 0) + break; + table_element = nullptr; + } + + if (table_element == nullptr) + { + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + + status = lxb_dom_elements_by_tag_name(table_element, + tr_collection, (const lxb_char_t *)"tr", 2); + if (status != LXB_STATUS_OK && lxb_dom_collection_length(tr_collection) < 2) + { + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + + // skip row 0 , since it has the previous folder header + for (size_t i = 2; i < lxb_dom_collection_length(tr_collection); i++) + { + DirEntry entry; + std::string title, aclass; + memset(&entry.modified, 0, sizeof(DateTime)); + + tr_element = lxb_dom_collection_element(tr_collection, i); + + td_collection = lxb_dom_collection_make(&document->dom_document, 5); + status = lxb_dom_elements_by_tag_name(tr_element, + td_collection, (const lxb_char_t *)"td", 2); + if (status != LXB_STATUS_OK || lxb_dom_collection_length(td_collection) < 3) + { + lxb_dom_collection_destroy(td_collection, true); + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + + // td0 contains the tag + td_element = lxb_dom_collection_element(td_collection, 0); + lxb_dom_node_t *a_node = NextChildElement(td_element); + value = lxb_dom_element_local_name(lxb_dom_interface_element(a_node), &value_len); + tmp_string = std::string((const char *)value, value_len); + if (tmp_string.compare("a") != 0) + { + lxb_dom_collection_destroy(td_collection, true); + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + goto finish; + } + value = lxb_dom_element_get_attribute(lxb_dom_interface_element(a_node), (const lxb_char_t *)"href", 4, &value_len); + tmp_string = std::string((const char *)value, value_len); + if (tmp_string[tmp_string.length()-1] == '/') + tmp_string = tmp_string.substr(0, tmp_string.length()-1); + tmp_string = BaseClient::UnEscape(tmp_string); + sprintf(entry.name, "%s", tmp_string.c_str()); + sprintf(entry.directory, "%s", path.c_str()); + if (path.length() > 0 && path[path.length() - 1] == '/') + { + sprintf(entry.path, "%s%s", path.c_str(), entry.name); + } + else + { + sprintf(entry.path, "%s/%s", path.c_str(), entry.name); + } + + // next td contains the date + td_element = lxb_dom_collection_element(td_collection, 1); + value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len); + tmp_string = std::string((const char *)value, value_len); + std::vector date_time = Util::Split(tmp_string, " "); + + if (date_time.size() > 1) + { + std::vector adate = Util::Split(date_time[0], "-"); + if (adate.size() == 3) + { + entry.modified.day = atoi(adate[0].c_str()); + entry.modified.month = month_map[adate[1]]; + entry.modified.year = atoi(adate[2].c_str()); + } + + std::vector atime = Util::Split(date_time[1], ":"); + if (atime.size() == 2) + { + entry.modified.hours = atoi(atime[0].c_str()); + entry.modified.minutes = atoi(atime[1].c_str()); + } + } + + // next td contains file size, if fize size is "-", then it's a directory + td_element = lxb_dom_collection_element(td_collection, 2); + value = lxb_dom_node_text_content(NextChildTextNode(td_element), &value_len); + tmp_string = std::string((const char *)value, value_len); + + if (tmp_string.compare("-") == 0) + { + entry.isDir = true; + entry.selectable = true; + entry.file_size = 0; + sprintf(entry.display_size, "%s", lang_strings[STR_FOLDER]); + } + else + { + entry.isDir = false; + entry.selectable = true; + uint64_t multiplier = 0; + float fsize = std::stof(tmp_string.substr(0, tmp_string.size()-1)); + switch (tmp_string[tmp_string.size()-1]) { + case 'B': + multiplier = 1; + break; + case 'K': + multiplier = 1024; + break; + case 'M': + multiplier = 1048576; + break; + case 'G': + multiplier = 1073741824; + break; + default: + multiplier = 1; + } + entry.file_size = fsize * multiplier; + DirEntry::SetDisplaySize(&entry); + } + + lxb_dom_collection_destroy(td_collection, true); + out.push_back(entry); + } + + lxb_dom_collection_destroy(tr_collection, true); + lxb_dom_collection_destroy(table_collection, true); + lxb_html_document_destroy(document); + } + else + { + sprintf(this->response, "%s", httplib::to_string(res.error()).c_str()); + return out; + } + +finish: + return out; +} \ No newline at end of file diff --git a/source/clients/archiveorg.h b/source/clients/archiveorg.h new file mode 100644 index 0000000..34cdfbc --- /dev/null +++ b/source/clients/archiveorg.h @@ -0,0 +1,17 @@ +#ifndef EZ_ARCHIVEORG_H +#define EZ_ARCHIVEORG_H + +#include +#include +#include "http/httplib.h" +#include "clients/remote_client.h" +#include "clients/baseclient.h" +#include "common.h" + +class ArchiveOrgClient : public BaseClient +{ +public: + std::vector ListDir(const std::string &path); +}; + +#endif \ No newline at end of file diff --git a/source/config.cpp b/source/config.cpp index 8d359a7..946f47f 100644 --- a/source/config.cpp +++ b/source/config.cpp @@ -159,7 +159,7 @@ namespace CONFIG "Hungarian", "Indonesian", "Italiano", "Japanese", "Korean", "Polish", "Portuguese_BR", "Russian", "Romanian", "Ryukyuan", "Spanish", "Turkish", "Simplified Chinese", "Traditional Chinese", "Thai", "Ukrainian"}; - http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE, HTTP_SERVER_RCLONE}; + http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE, HTTP_SERVER_RCLONE, HTTP_SERVER_ARCHIVEORG}; text_file_extensions = { ".txt", ".ini", ".log", ".json", ".xml", ".html", ".xhtml", ".conf", ".config" }; image_file_extensions = { ".bmp", ".jpg", ".jpeg", ".png", ".webp" }; diff --git a/source/config.h b/source/config.h index a2179b8..c8324f9 100644 --- a/source/config.h +++ b/source/config.h @@ -82,6 +82,7 @@ #define HTTP_SERVER_NGINX "Nginx" #define HTTP_SERVER_NPX_SERVE "Serve" #define HTTP_SERVER_RCLONE "RClone" +#define HTTP_SERVER_ARCHIVEORG "Archive.org" #define MAX_EDIT_FILE_SIZE 32768 diff --git a/source/server/http_server.cpp b/source/server/http_server.cpp index 8ffca3e..b2ac4a0 100644 --- a/source/server/http_server.cpp +++ b/source/server/http_server.cpp @@ -10,6 +10,7 @@ #include "clients/nfsclient.h" #include "clients/webdav.h" #include "clients/apache.h" +#include "clients/archiveorg.h" #include "clients/iis.h" #include "clients/nginx.h" #include "clients/npxserve.h" @@ -249,6 +250,8 @@ namespace HttpServer tmp_client = new NpxServeClient(); else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_RCLONE) == 0) tmp_client = new RCloneClient(); + else if (strcmp(remote_settings->http_server_type, HTTP_SERVER_ARCHIVEORG) == 0) + tmp_client = new ArchiveOrgClient(); } if (tmp_client->clientType() != CLIENT_TYPE_GOOGLE) diff --git a/source/windows.cpp b/source/windows.cpp index 7cb5805..accdf59 100644 --- a/source/windows.cpp +++ b/source/windows.cpp @@ -2593,6 +2593,10 @@ namespace Windows if (ime_result == IME_DIALOG_RESULT_FINISHED) { CONFIG::SetClientType(remote_settings); + if (strncasecmp(remote_settings->server, "https://archive.org/", 20) == 0) + { + sprintf(remote_settings->http_server_type, "%s", HTTP_SERVER_ARCHIVEORG); + } } }