Files
OrcaSlicer/src/slic3r/plugin/CloudPluginService.cpp
T
HanifKoh 84657ff11e Add Missing Includes Across the Remaining Sources and Tests (#16071)
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy

Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp.

* Ignore minilzo's Config Headers in clang-tidy

lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes.

* Add Missing Includes Across the Remaining Sources and Tests

Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo.

* Make the GUI and Test Headers Compile on Their Own

Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone.

* Keep Windows and nanosvg Setup Ahead of the Added Includes

OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build.

* Add the GUI Includes the First Pass Missed

Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass.

* Keep the Added Test Includes Below the NOMINMAX Guard

test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
2026-10-03 13:45:21 +08:00

319 lines
9.9 KiB
C++

#include "CloudPluginService.hpp"
#include "OrcaCloudServiceAgent.hpp"
#include "slic3r/Utils/Http.hpp"
#include <boost/filesystem.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/fstream.hpp>
#include <cstddef>
#include <memory>
#include <ios>
#include <slic3r/plugin/PluginDescriptor.hpp>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace Slic3r {
void CloudPluginService::set_cloud_agent(std::shared_ptr<OrcaCloudServiceAgent> agent) { m_orca_agent = std::move(agent); }
std::shared_ptr<OrcaCloudServiceAgent> CloudPluginService::get_cloud_agent() const { return m_orca_agent; }
bool CloudPluginService::can_fetch_cloud_plugins() const
{
if (!m_orca_agent) {
BOOST_LOG_TRIVIAL(warning) << "Orca service agent is null";
return false;
}
if (!m_orca_agent->is_user_login() || m_orca_agent->get_user_id().empty()) {
BOOST_LOG_TRIVIAL(info) << "User not logged in, no cloud directory";
return false;
}
return true;
}
bool CloudPluginService::fetch_manifests_into_descriptors(std::vector<PluginDescriptor>& descriptors,
std::vector<std::string>& not_found,
std::vector<std::string>& unauthorized) const
{
descriptors.clear();
not_found.clear();
unauthorized.clear();
if (m_orca_agent) {
int ret = m_orca_agent->fetch_subscribed_manifests_into_descriptors(descriptors, not_found, unauthorized);
if (ret != 0) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Failed to get subscribed plugins.";
return false;
}
std::vector<PluginDescriptor> mine_descriptors;
ret = m_orca_agent->fetch_mine_manifests_into_descriptors(mine_descriptors);
if (ret != 0) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Failed to get owned plugins.";
return false;
}
descriptors.insert(descriptors.end(), mine_descriptors.begin(), mine_descriptors.end());
}
return true;
}
bool CloudPluginService::request_cloud_subscribe(const std::string& plugin_uuid, std::string& error) const
{
error.clear();
if (!m_orca_agent) {
error = "No cloud agent.";
return false;
}
if (plugin_uuid.empty()) {
error = "Cloud plugin key is missing UUID.";
return false;
}
if (m_orca_agent->subscribe_plugin(plugin_uuid) != 0) {
error = "Failed to subscribe to cloud plugin, see logs for more info.";
return false;
}
return true;
}
bool CloudPluginService::request_cloud_unsubscribe(const PluginDescriptor& plugin, std::string& error) const
{
if (!m_orca_agent) {
error = "No cloud agent.";
return false;
}
if (!plugin.is_cloud_plugin()) {
error = "Only cloud plugins can be unsubscribed.";
return false;
}
const std::string cloud_uuid = plugin.cloud_uuid();
if (cloud_uuid.empty()) {
error = "Cloud plugin key is missing UUID.";
return false;
}
int result = m_orca_agent->unsubscribe_plugins({cloud_uuid});
if (result != 0) {
error = "Failed to unsubscribe plugin, see logs for more info.";
return false;
}
return true;
}
bool CloudPluginService::download_cloud_plugin(PluginDescriptor& entry,
const std::string& requested_version,
CloudPluginDownload& download,
std::string& error) const
{
namespace fs = boost::filesystem;
error.clear();
download = {};
// Look up the cloud plugin metadata in the catalog.
std::string download_url;
if (!entry.is_cloud_plugin()) {
error = "Plugin is not a cloud plugin";
return false;
}
const std::string entry_uuid = entry.cloud_uuid();
if (!m_orca_agent) {
error = "Cloud service agent is null";
return false;
}
std::vector<PluginDownloadData> data;
std::vector<PluginDownloadNotFound> not_found;
std::vector<std::string> unauthorized;
int result = m_orca_agent->get_plugin_download_url(entry_uuid, requested_version, data, not_found, unauthorized);
if (result != 0) {
error = "Failed to fetch download_url, result =" + std::to_string(result);
return false;
}
for (const std::string& plugin_uuid : unauthorized) {
if (plugin_uuid == entry_uuid) {
error = "You are not authorized to download this cloud plugin.";
return false;
}
}
for (const PluginDownloadNotFound& item : not_found) {
if (item.id == entry_uuid) {
if (item.reason == "requested os not found")
error = "No plugin package is available for this operating system.";
else if (item.reason == "requested version not found")
error = requested_version.empty() ? "The selected plugin version was not found." :
"Plugin version " + requested_version + " was not found.";
else if (item.reason == "requested plugin not found")
error = "Cloud plugin was not found.";
else if (!item.reason.empty())
error = "Cloud plugin download was not found: " + item.reason + ".";
else
error = "Cloud plugin download was not found.";
return false;
}
}
for (const PluginDownloadData& item : data) {
if (item.plugin_id == entry_uuid) {
download_url = item.download_link;
break;
}
}
if (download_url.empty()) {
error = "No download URL is available for this plugin. Please "
"check the logs for errors.";
return false;
}
// Download the plugin package.
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Downloading cloud plugin " << entry_uuid << " from " << download_url;
std::string body;
unsigned http_status = 0;
Http http = Http::get(download_url);
http.timeout_connect(30)
.timeout_max(300)
.on_complete([&body, &http_status](std::string response_body, unsigned status) {
body = std::move(response_body);
http_status = status;
})
.on_error([&error](std::string response_body, std::string err, unsigned status) {
error = std::move(err);
if (!response_body.empty())
error += " — " + response_body;
})
.perform_sync();
if (!error.empty()) {
error = "Failed to download plugin: " + error;
return false;
}
if (http_status >= 400) {
error = "Plugin download failed with HTTP status " + std::to_string(http_status);
return false;
}
if (body.empty()) {
error = "Plugin download returned empty data.";
return false;
}
// Detect file format from content: PK magic bytes = zip/wheel, otherwise .py.
const bool is_wheel = (body.size() >= 4 && body[0] == 'P' && body[1] == 'K' && body[2] == '\x03' && body[3] == '\x04');
const std::string ext = is_wheel ? ".whl" : ".py";
const fs::path tmp_path = fs::temp_directory_path() / (fs::unique_path("cloud_plugin-%%%%-%%%%-%%%%-%%%%%%%").string() + ext);
{
boost::nowide::ofstream file(tmp_path.string(), std::ios::binary | std::ios::trunc);
if (!file) {
error = "Failed to create temporary file for plugin download.";
return false;
}
file.write(body.data(), body.size());
if (!file) {
error = "Failed to write plugin data to temporary file.";
return false;
}
}
download.package_path = tmp_path;
if (entry.cloud.has_value())
entry.cloud->update_available = false;
return true;
}
bool CloudPluginService::fetch_plugin_changelog(const PluginDescriptor& descriptor,
std::vector<PluginChangelog>& changelog,
std::string& error) const
{
error.clear();
changelog.clear();
std::string plugin_uuid = descriptor.cloud_uuid();
std::vector<PluginDescriptor> descriptors{descriptor};
std::unordered_map<std::string, std::vector<PluginChangelog>> changelogs;
bool result = fetch_plugin_changelog(descriptors, changelogs, error);
bool found_changelog = changelogs.find(plugin_uuid) != changelogs.end();
if (changelogs.empty() || !found_changelog) {
if (!error.empty())
return false;
error = "Failed to fetch changelogs for plugin " + descriptor.cloud_uuid();
return false;
}
changelog = std::move(changelogs[plugin_uuid]);
return result;
}
bool CloudPluginService::fetch_plugin_changelog(const std::vector<PluginDescriptor>& descriptors,
std::unordered_map<std::string, std::vector<PluginChangelog>>& changelog,
std::string& error) const
{
error.clear();
changelog.clear();
if (!m_orca_agent) {
error = "Cloud service agent is null.";
return false;
}
size_t descriptor_count = descriptors.size();
if (descriptor_count <= 0) {
return true;
}
std::vector<std::string> uuids;
uuids.reserve(descriptor_count);
for (const PluginDescriptor& descriptor : descriptors) {
const std::string uuid = descriptor.cloud_uuid();
if (!uuid.empty())
uuids.push_back(uuid);
}
if (uuids.empty())
return true;
int result = m_orca_agent->fetch_plugin_changelogs(uuids, changelog);
if (result != 0) {
error = "Failed to fetch one or more plugin changelogs. result=" + std::to_string(result);
BOOST_LOG_TRIVIAL(warning) << error;
return false;
}
return true;
}
} // namespace Slic3r