#include #include #include "http/httplib.h" #include "server/http_server.h" #include "clients/remote_client.h" #include "clients/archiveorg.h" #include "clients/baseclient.h" #include "clients/ftpclient.h" #include "clients/nfsclient.h" #include "clients/smbclient.h" #include "clients/sftpclient.h" #include "clients/webdav.h" #include "config.h" #include "fs.h" #include "util.h" #define SUCCESS_MSG "{ \"result\": { \"success\": true, \"error\": null } }" #define FAILURE_MSG "{ \"result\": { \"success\": false, \"error\": \"%s\" } }" #define SUCCESS_MSG_LEN 48 #define PKG_INITIAL_REQUEST_SIZE 8388608ul using namespace httplib; Server *svr; int http_server_port = 6701; static pthread_t bg_download_thread; static uint64_t g_dl_offset; static bool stop_download = false; namespace HttpServer { static int FtpCallback(int64_t xfered, void *arg) { return 1; } static int DownloadFtpCallback(int64_t xfered, void *arg) { *g_bytes_transfered = g_dl_offset + xfered; return 1; } std::string dump_headers(const Headers &headers) { std::string s; char buf[BUFSIZ]; for (auto it = headers.begin(); it != headers.end(); ++it) { const auto &x = *it; snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); s += buf; } return s; } std::string log(const Request &req, const Response &res) { std::string s; char buf[BUFSIZ]; s += "================================\n"; snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(), req.version.c_str(), req.path.c_str()); s += buf; std::string query; for (auto it = req.params.begin(); it != req.params.end(); ++it) { const auto &x = *it; snprintf(buf, sizeof(buf), "%c%s=%s", (it == req.params.begin()) ? '?' : '&', x.first.c_str(), x.second.c_str()); query += buf; } snprintf(buf, sizeof(buf), "%s\n", query.c_str()); s += buf; s += dump_headers(req.headers); s += "--------------------------------\n"; snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str()); s += buf; s += dump_headers(res.headers); s += "\n"; if (!res.body.empty()) { s += res.body; } s += "\n"; return s; } void failed(Response &res, int status, const std::string &msg) { res.status = status; char response_msg[msg.length() + strlen(FAILURE_MSG) + 2]; snprintf(response_msg, sizeof(response_msg), "{ \"result\": { \"success\": false, \"error\": \"%s\" } }", msg.c_str()); res.set_content(response_msg, strlen(response_msg), "application/json"); return; } void bad_request(Response &res, const std::string &msg) { failed(res, 200, msg); return; } void success(Response &res) { res.status = 200; res.set_content(SUCCESS_MSG, SUCCESS_MSG_LEN, "application/json"); return; } static RemoteClient *GetRemoteClient(HostInfo *host_info) { RemoteClient *tmp_client = nullptr; if (host_info->type == CLIENT_TYPE_HTTP_SERVER) { if (host_info->http_server_type.compare(HTTP_SERVER_ARCHIVEORG)) { tmp_client = new ArchiveOrgClient(); } else { tmp_client = new BaseClient(); } } else if (host_info->type == CLIENT_TYPE_WEBDAV) { tmp_client = new WebDAVClient(); } else if (host_info->type == CLIENT_TYPE_FILEHOST) { tmp_client = new BaseClient(); } else if (host_info->type == CLIENT_TYPE_SMB) { tmp_client = new SmbClient(); } else if (host_info->type == CLIENT_TYPE_SFTP) { tmp_client = new SFTPClient(); } else if (host_info->type == CLIENT_TYPE_NFS) { tmp_client = new NfsClient(); } else if (host_info->type == CLIENT_TYPE_FTP) { tmp_client = new FtpClient(); FtpClient *ftp_client = (FtpClient*) tmp_client; ftp_client->SetCallbackXferFunction(FtpCallback); } if (tmp_client != nullptr) tmp_client->Connect(host_info->url, host_info->username, host_info->password); return tmp_client; } static void DeleteRemoteClient(RemoteClient *tmp_client) { tmp_client->Quit(); delete tmp_client; } void *DownloadFilesThread(void *argp) { char temp_file[2049]; uint64_t tmp_file_size; int ret; while (!stop_download) { for (int i=0; i < bg_download_list.size(); i++) { if (bg_download_list[i].state == STATE_PENDING) { RemoteClient *tmp_client = GetRemoteClient(&(bg_download_list[i].host_info)); if (tmp_client == nullptr) { break; } g_bytes_transfered = &(bg_download_list[i].bytes_transfered); if (bg_download_list[i].host_info.type == CLIENT_TYPE_FTP) { FtpClient *ftpclient = (FtpClient*)tmp_client; g_dl_offset = 0; ftpclient->SetCallbackBytes(1); ftpclient->SetCallbackXferFunction(DownloadFtpCallback); } bg_download_list[i].state = STATE_DOWNLOADING; CONFIG::SaveBgDownloadData(); snprintf(temp_file, sizeof(temp_file), "%s.tmp", bg_download_list[i].dest_path.c_str()); Util::Notify("Started download %s", bg_download_list[i].dest_path.c_str()); ret = tmp_client->Get(temp_file, bg_download_list[i].src_path); if (ret == 0) { bg_download_list[i].state = STATE_FAILED; Util::Notify("Failed to download %s", bg_download_list[i].dest_path.c_str()); } else { FS::Rename(temp_file, bg_download_list[i].dest_path); Util::Notify("Completed download %s", bg_download_list[i].dest_path.c_str()); bg_download_list[i].state = STATE_SUCCESS; } CONFIG::SaveBgDownloadData(); DeleteRemoteClient(tmp_client); } else if (bg_download_list[i].state == STATE_DOWNLOADING || bg_download_list[i].state == STATE_FAILED) { // Resume interrupted download RemoteClient *tmp_client = GetRemoteClient(&(bg_download_list[i].host_info)); g_bytes_transfered = &(bg_download_list[i].bytes_transfered); if (bg_download_list[i].host_info.type == CLIENT_TYPE_FTP) { FtpClient *ftpclient = (FtpClient*)tmp_client; ftpclient->SetCallbackBytes(1); ftpclient->SetCallbackXferFunction(DownloadFtpCallback); } bg_download_list[i].state = STATE_RESUMED; snprintf(temp_file, sizeof(temp_file), "%s.tmp", bg_download_list[i].dest_path.c_str()); // Check if temp file still exists, if exists then resume download Util::Notify("Resuming download %s", bg_download_list[i].dest_path.c_str()); if (FS::FileExists(temp_file)) { tmp_file_size = FS::GetSize(temp_file); g_dl_offset = tmp_file_size; ret = tmp_client->Get(temp_file, bg_download_list[i].src_path, tmp_file_size); } else { g_dl_offset = 0; ret = tmp_client->Get(temp_file, bg_download_list[i].src_path); } if (ret == 0) { bg_download_list[i].state = STATE_FAILED; Util::Notify("Failed to download %s", bg_download_list[i].dest_path.c_str()); } else { FS::Rename(temp_file, bg_download_list[i].dest_path); Util::Notify("Completed download %s", bg_download_list[i].dest_path.c_str()); bg_download_list[i].state = STATE_SUCCESS; } CONFIG::SaveBgDownloadData(); DeleteRemoteClient(tmp_client); } } sleep(1); } return nullptr; } void *ServerThread(void *argp) { svr->Get("/", [&](const Request &req, Response &res) { res.set_redirect("/index.html"); }); svr->Post("/store_bg_install_data", [&](const Request &req, Response &res) { const char *hash_param; const char *path_param; const char *url_param; const char *username_param; const char *password_param; const char *http_server_type_param; int type_param; json_object *jobj = json_tokener_parse(req.body.c_str()); if (jobj != nullptr) { hash_param = json_object_get_string(json_object_object_get(jobj, "hash")); url_param = json_object_get_string(json_object_object_get(jobj, "url")); path_param = json_object_get_string(json_object_object_get(jobj, "path")); username_param = json_object_get_string(json_object_object_get(jobj, "username")); password_param = json_object_get_string(json_object_object_get(jobj, "password")); http_server_type_param = json_object_get_string(json_object_object_get(jobj, "http_server_type")); type_param = json_object_get_int(json_object_object_get(jobj, "type")); if (url_param == nullptr || hash_param == nullptr) { bad_request(res, "Required url_param or hash parameter missing"); return; } PackageInstallData pkg_data; pkg_data.host_info.url = url_param; if (username_param != nullptr) pkg_data.host_info.username = username_param; if (password_param != nullptr) pkg_data.host_info.password = password_param; if (path_param != nullptr) pkg_data.path = path_param; if (http_server_type_param != nullptr) pkg_data.host_info.http_server_type = http_server_type_param; pkg_data.timestamp = Util::GetTick(); pkg_data.host_info.type = type_param; pkg_data.client = nullptr; CONFIG::AddPackageInstallHostData(hash_param, pkg_data); CONFIG::SavePackageInstallHostData(); } }); svr->Get("/ls", [&](const Request &req, Response &res) { std::vector files = FS::ListFiles("/"); std::string out; for (int i=0; i < files.size(); i++) { out.append(files[i]).append("\n"); } res.set_content(out, "text/plain"); }); svr->Get("/bg_install/(.*)", [&](const Request &req, Response &res) { std::string hash = req.matches[1]; PackageInstallData* pkg_host_data = CONFIG::GetPackageInstallHostData(hash); RemoteClient *tmp_client; if (pkg_host_data == nullptr) { failed(res, 500, "Cannot resume background install of " + hash + ". Host data not found."); return; } tmp_client = GetRemoteClient(&(pkg_host_data->host_info)); if (tmp_client == nullptr) { res.status = 500; return; } std::string path = pkg_host_data->path; /* if (req.method == "HEAD") { int64_t file_size; int ret; res.status = 204; res.set_header("Content-Length", std::to_string(pkg_host_data->file_size)); res.set_header("Accept-Ranges", "bytes"); DeleteRemoteClient(tmp_client); return; } */ if (req.ranges.empty()) { res.status = 200; res.set_content_provider( (1024*128), "application/octet-stream", [tmp_client, path](size_t offset, size_t length, DataSink &sink) { int ret = tmp_client->GetRange(path, sink, length, offset); return (ret == 1); }, [tmp_client, path](bool success) { DeleteRemoteClient(tmp_client); }); } else { res.status = 206; size_t range_len = (req.ranges[0].second - req.ranges[0].first) + 1; if (req.ranges[0].second >= 18000000000000000000ul) { range_len = PKG_INITIAL_REQUEST_SIZE; res.set_header("Content-Length", std::to_string(range_len)); res.set_header("Content-Range", std::string("bytes ") + std::to_string(req.ranges[0].first)+"-" + std::to_string(req.ranges[0].first+PKG_INITIAL_REQUEST_SIZE-1) + "/"+std::to_string(range_len)); } std::pair range = req.ranges[0]; res.set_content_provider( range_len, "application/octet-stream", [tmp_client, path, range, range_len](size_t offset, size_t length, DataSink &sink) { int ret; ret = tmp_client->GetRange(path, sink, range_len, range.first); return (ret==1); }, [tmp_client, path, range](bool success) { DeleteRemoteClient(tmp_client); }); } }); svr->Post("/download_url", [&](const Request &req, Response &res) { int type_param; const char *url_param; const char *username_param; const char *password_param; const char *http_server_type_param; const char *src_path_param; const char *dest_path_param; uint64_t file_size_param; uint64_t id_param; json_object *jobj = json_tokener_parse(req.body.c_str()); if (jobj != nullptr) { type_param = json_object_get_int(json_object_object_get(jobj, "type")); url_param = json_object_get_string(json_object_object_get(jobj, "url")); username_param = json_object_get_string(json_object_object_get(jobj, "username")); password_param = json_object_get_string(json_object_object_get(jobj, "password")); http_server_type_param = json_object_get_string(json_object_object_get(jobj, "http_server_type")); src_path_param = json_object_get_string(json_object_object_get(jobj, "src_path")); dest_path_param = json_object_get_string(json_object_object_get(jobj, "dest_path")); file_size_param = json_object_get_uint64(json_object_object_get(jobj, "size")); id_param = json_object_get_uint64(json_object_object_get(jobj, "id")); if (url_param == nullptr || src_path_param == nullptr || dest_path_param == nullptr) { bad_request(res, "Required parameters are missing"); return; } BgDownloadData download_data; download_data.host_info.type = type_param; download_data.host_info.url = url_param; download_data.src_path = src_path_param; download_data.dest_path = dest_path_param; download_data.file_size = file_size_param; download_data.state = STATE_PENDING; download_data.id = id_param; download_data.bytes_transfered = 0; download_data.timestamp = Util::GetTick(); if (username_param != nullptr) download_data.host_info.username = username_param; if (password_param != nullptr) download_data.host_info.password = password_param; if (http_server_type_param != nullptr) download_data.host_info.http_server_type = http_server_type_param; CONFIG::AddBgDownloadData(download_data); CONFIG::SaveBgDownloadData(); } }); svr->Get("/get_download_state", [&](const Request &req, Response &res) { json_object *download_list = json_object_new_array(); for (int i=0; i < bg_download_list.size(); i++) { json_object *download_item_obj = json_object_new_object(); json_object_object_add(download_item_obj, "path", json_object_new_string(bg_download_list[i].dest_path.c_str())); json_object_object_add(download_item_obj, "bytes_transfered", json_object_new_uint64(bg_download_list[i].bytes_transfered)); json_object_object_add(download_item_obj, "file_size", json_object_new_uint64(bg_download_list[i].file_size)); json_object_object_add(download_item_obj, "state", json_object_new_int(bg_download_list[i].state)); json_object_object_add(download_item_obj, "timestamp", json_object_new_uint64(bg_download_list[i].timestamp/1000000)); json_object_array_add(download_list, download_item_obj); } const char *payload_str = json_object_to_json_string(download_list); res.status = 200; res.set_content(payload_str, "application/json"); }); svr->Get("/stop", [&](const Request & /*req*/, Response & /*res*/) { svr->stop(); }); svr->Get("/version", [&](const Request & req, Response &res) { res.status = 200; char version[20]; sprintf(version, "%.2f", EZREMOTE_VERSION); res.set_content(version, "text/html"); }); svr->set_error_handler([](const Request & /*req*/, Response &res) { const char *fmt = "

Error Status: %d

"; char buf[BUFSIZ]; snprintf(buf, sizeof(buf), fmt, res.status); res.set_content(buf, "text/html"); }); /* svr->set_logger([](const Request &req, const Response &res) { dbglogger_log("%s", log(req, res).c_str()); }); */ svr->set_payload_max_length(1024 * 1024 * 12); svr->set_tcp_nodelay(true); svr->set_mount_point("/", "/"); svr->listen("0.0.0.0", http_server_port); return NULL; } void Start() { if (svr == nullptr) svr = new Server(); if (!svr->is_valid()) { return; } Util::Notify("Starting ezRemote Server %.2f on port %d", EZREMOTE_VERSION, http_server_port); ServerThread(nullptr); } void Stop() { if (svr != nullptr) svr->stop(); } void StartDownloadThread() { pthread_create(&bg_download_thread, NULL, DownloadFilesThread, NULL); } void StopDownloadThread() { stop_download = true; pthread_cancel(bg_download_thread); } bool IsStarted() { httplib::Client client = httplib::Client("http://127.0.0.1:6701"); if (auto res = client.Get("/version")) { return true; } return false; } }