#include "pch.h" #include "legacy_cloud_services.h" #include "app.h" #include "canvas.h" #include "legacy_app_dialog_services.h" #include "legacy_canvas_view_services.h" #include "legacy_document_session_services.h" #include "legacy_document_open_services.h" #include "legacy_ui_overlay_services.h" #include "node_dialog_cloud.h" #include "node_progress_bar.h" #include "util.h" namespace pp::panopainter { pp::foundation::Status execute_legacy_cloud_download_selection_action( App& app, pp::app::CloudDownloadSelectionAction action, NodeDialogCloud& dialog); namespace { #if WITH_CURL int progress_callback_download(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { (void)ultotal; (void)ulnow; auto* progress = static_cast*>(clientp); const auto plan = pp::app::plan_cloud_transfer_progress( static_cast(dltotal), static_cast(dlnow)); if (progress != nullptr && *progress && plan.notify) (*progress)(plan.fraction); return 0; } int progress_callback_upload(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { (void)dltotal; (void)dlnow; auto* progress = static_cast*>(clientp); const auto plan = pp::app::plan_cloud_transfer_progress( static_cast(ultotal), static_cast(ulnow)); if (progress != nullptr && *progress && plan.notify) (*progress)(plan.fraction); return 0; } struct curl_httppost* create_cloud_upload_form(const std::string& filename) { struct curl_httppost* formpost = NULL; struct curl_httppost* lastptr = NULL; curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "fileToUpload", CURLFORM_FILE, filename.c_str(), CURLFORM_END); return formpost; } void handle_cloud_upload_transfer_result(CURLcode err, const std::string& res) { (void)err; std::cout << "\n\nUPLOAD RESULT\n" << res << "\n\n\n"; } void execute_cloud_download_transfer( App& app, std::string url, std::string dest_filepath, std::function progress); void execute_cloud_download_flow( App& app, const pp::app::CloudDownloadRequest& request); std::shared_ptr create_cloud_download_progress_dialog(App& app) { return pp::panopainter::create_legacy_app_message_dialog( app, pp::app::plan_cloud_download_progress_prompt()); } void execute_cloud_download_transfer_with_progress_dialog( App& app, const pp::app::CloudDownloadRequest& request, const std::shared_ptr& dialog) { const std::string url = "https://panopainter.com/cloud/cloud-dwl.php?file=" + request.selected_file; execute_cloud_download_transfer(app, url, request.selected_path, [dialog](float p) { const auto progress = pp::app::format_cloud_download_progress_message(p); dialog->m_message->set_text(progress.c_str()); }); } void execute_cloud_download_transfer( App& app, std::string url, std::string dest_filepath, std::function progress) { const auto plan = pp::app::plan_cloud_download_transfer( url, dest_filepath, progress != nullptr, app.disables_network_tls_verification()); if (plan.action != pp::app::CloudTransferAction::start_transfer) { LOG("download skipped: invalid transfer request"); return; } CURL* curl = curl_easy_init(); if (curl) { FILE* fp = fopen(dest_filepath.c_str(), "wb"); if (fp == nullptr) { LOG("download failed to open destination %s", dest_filepath.c_str()); curl_easy_cleanup(curl); return; } LOG("download %s to %s", url.c_str(), dest_filepath.c_str()); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_write); if (plan.disable_tls_verification) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); if (plan.enable_progress) { curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_download); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); } auto err = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); } } void open_downloaded_project_and_close_dialog( App& app, const pp::app::CloudDownloadRequest& request, const std::shared_ptr& dialog) { execute_legacy_downloaded_project_open(app, request.selected_path, request.selected_name); pp::panopainter::close_legacy_dialog_node(*dialog); } void execute_cloud_download_flow( App& app, const pp::app::CloudDownloadRequest& request) { auto m = create_cloud_download_progress_dialog(app); execute_cloud_download_transfer_with_progress_dialog(app, request, m); open_downloaded_project_and_close_dialog(app, request, m); } void execute_cloud_upload_transfer( App& app, std::string filename, std::string name, std::function progress) { const auto plan = pp::app::plan_cloud_upload_transfer( filename, progress != nullptr, app.disables_network_tls_verification()); if (plan.action != pp::app::CloudTransferAction::start_transfer) { LOG("upload skipped: invalid transfer request"); return; } CURL* curl; auto* formpost = create_cloud_upload_form(filename); //curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); std::string res; if (curl) { std::string url = "https://panopainter.com/cloud/cloud-upl.php?name=" + name; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler); if (plan.disable_tls_verification) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); if (plan.enable_progress) { curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_upload); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); } auto err = curl_easy_perform(curl); handle_cloud_upload_transfer_result(err, res); curl_easy_cleanup(curl); } } void execute_cloud_download_thread( App& app, const pp::app::CloudDownloadRequest& request) { BT_SetTerminate(); execute_cloud_download_flow(app, request); } void launch_cloud_download_thread( App& app, const pp::app::CloudDownloadRequest& request) { std::thread([app = &app, request] { execute_cloud_download_thread(*app, request); }).detach(); } void execute_cloud_publish_worker(App& app, bool save_before_upload) { BT_SetTerminate(); if (save_before_upload) { execute_legacy_document_save_before_cloud_upload(app); } const auto progress_plan = pp::app::plan_cloud_upload_progress_dialog(); auto pb = app.show_progress(progress_plan.title, progress_plan.total); execute_cloud_upload_transfer(app, app.doc_path, app.doc_filename, [pb](float p) { pb->set_progress(p); }); pp::panopainter::close_legacy_dialog_node(*pb); const auto success_plan = pp::app::plan_cloud_upload_success_prompt(); app.message_box(success_plan.title, success_plan.message, success_plan.show_cancel); } void wire_cloud_publish_prompt_buttons( const std::shared_ptr& dialog, std::function upload_thread) { dialog->btn_ok->on_click = [dialog, upload_thread](Node*) { std::thread(upload_thread).detach(); pp::panopainter::close_legacy_dialog_node(*dialog); }; dialog->btn_cancel->on_click = [dialog](Node*) { pp::panopainter::close_legacy_dialog_node(*dialog); }; } void show_cloud_publish_prompt(App& app, bool save_before_upload) { auto upload_thread = [&app, save_before_upload] { execute_cloud_publish_worker(app, save_before_upload); }; const auto prompt_plan = pp::app::plan_cloud_publish_prompt(); auto dialog = app.message_box(prompt_plan.title, prompt_plan.message, prompt_plan.show_cancel); wire_cloud_publish_prompt_buttons(dialog, upload_thread); } std::shared_ptr create_cloud_bulk_upload_progress( App& app, int progress_total) { const auto progress_plan = pp::app::plan_cloud_bulk_upload_progress_dialog(progress_total); return app.show_progress(progress_plan.title, progress_plan.total); } void execute_cloud_bulk_upload_files( App& app, const std::vector& names, const std::shared_ptr& bulk_progress) { [[maybe_unused]] gl_state gl; for (const auto& n : names) { std::string path = app.data_path + "/" + n; execute_cloud_upload_transfer(app, path, std::string(), std::function {}); if (bulk_progress) { bulk_progress->increment(); } } } void close_cloud_bulk_upload_progress(std::shared_ptr& progress) { if (progress) { pp::panopainter::close_legacy_dialog_node(*progress); } progress.reset(); } #endif //WITH_CURL void wire_cloud_browser_ok_button( const std::shared_ptr& dialog, App* app) { dialog->btn_ok->on_click = [app, dialog](Node*) { const auto selection_plan = pp::app::plan_cloud_download_selection(dialog->selected_file); const auto status = execute_legacy_cloud_download_selection_action(*app, selection_plan, *dialog); if (!status.ok()) LOG("Cloud download selection action failed: %s", status.message); }; } void show_cloud_browser_dialog(App& app) { auto dialog = pp::panopainter::create_legacy_cloud_browser_dialog_overlay(app); wire_cloud_browser_ok_button(dialog, &app); } void show_cloud_save_required_warning(App& app); class LegacyCloudServices final : public pp::app::CloudServices { public: explicit LegacyCloudServices(App& app) noexcept : app_(app) { } void show_save_required_warning() override { show_cloud_save_required_warning(app_); } void prompt_publish(bool save_before_upload) override { show_cloud_publish_prompt(app_, save_before_upload); } void begin_bulk_upload(int progress_total, bool show_progress) override { bulk_progress_.reset(); if (show_progress) { bulk_progress_ = create_cloud_bulk_upload_progress(app_, progress_total); } } void upload_all_bulk_files() override { auto names = Asset::list_files(app_.data_path, ".*\\.ppi"); execute_cloud_bulk_upload_files(app_, names, bulk_progress_); } void end_bulk_upload() override { close_cloud_bulk_upload_progress(bulk_progress_); } void show_browser() override { show_cloud_browser_dialog(app_); } void start_download(const pp::app::CloudDownloadRequest& request) override { launch_cloud_download_thread(app_, request); } private: App& app_; std::shared_ptr bulk_progress_; }; void show_cloud_save_required_warning(App& app) { const auto plan = pp::app::plan_cloud_save_required_prompt(); app.message_box(plan.title, plan.message, plan.show_cancel); } } // namespace pp::foundation::Status execute_legacy_cloud_upload_plan( App& app, const pp::app::CloudUploadPlan& plan) { LegacyCloudServices services(app); return pp::app::execute_cloud_upload_plan(plan, services); } pp::foundation::Status execute_legacy_cloud_bulk_upload_plan( App& app, const pp::app::CloudBulkUploadPlan& plan) { LegacyCloudServices services(app); return pp::app::execute_cloud_bulk_upload_plan(plan, services); } pp::foundation::Status execute_legacy_cloud_browse_action( App& app, pp::app::CloudBrowseAction action) { LegacyCloudServices services(app); return pp::app::execute_cloud_browse_action(action, services); } pp::foundation::Status execute_legacy_cloud_download_selection_action( App& app, pp::app::CloudDownloadSelectionAction action, NodeDialogCloud& dialog) { pp::app::CloudDownloadRequest request { .selected_file = dialog.selected_file, .selected_path = dialog.selected_path, .selected_name = dialog.selected_name, }; if (action == pp::app::CloudDownloadSelectionAction::start_download) { pp::panopainter::close_legacy_dialog_node(dialog); } LegacyCloudServices services(app); return pp::app::execute_cloud_download_selection_action(action, services, request); } } // namespace pp::panopainter